【実務の極致】データ消失の恐怖を設計でねじ伏せる:AutoCAD VBAにおける「フォールバック保存」の実装
エンジニアとして、プログラムを書くこと自体は仕事の半分に過ぎない。残りの半分は、「想定外の事態が起きたときに、ユーザーの資産(データ)をいかに守り抜くか」という設計思想の証明だ。
AutoCADの自動化ツールを開発している際、最も恐ろしいシナリオは何か? それは、数時間、あるいは数日かけて積み上げた図面データが、ネットワークの瞬断やサーバーのタイムアウトによって、保存プロセス中に消失することだ。
一般的な開発者は、`On Error Resume Next` でエラーを握りつぶすか、単に `MsgBox` でエラーを表示して終了する。そんなものは、プロの仕事ではない。プロのエンジニアは、「一回目の試行が失敗することを前提とした、二段構えの防御策(フォールバック)」を設計に組み込む。
本記事では、ネットワーク切断という「不可避なエラー」に直面した際、自動的にローカル環境へデータを退避させる、堅牢な保存ロジックの設計手法を伝授する。
1. なぜ「単純なエラーハンドリング」では不十分なのか
多くの開発者が陥る罠は、`ActiveDocument.SaveAs` を呼び出し、エラーが出たら「保存に失敗しました」と表示して終わる設計だ。
' --- アンチパターン:素人のコード ---
Sub BadSave()
On Error Resume Next
ActiveDocument.SaveAs "Z:\Project\Drawing.dwg"
If Err.Number <> 0 Then
MsgBox "保存に失敗しました。データを閉じてください。" ' ← これが最悪の結末を招く
End If
End Sub
このコードの致命的な欠陥は、「エラーが起きた瞬間に、メモリ上の未保存データがユーザーのコントロールを離れるリスクを無視している」点にある。ネットワークが不安定な時、ユーザーはパニックに陥る。その時、ツールが提供すべきは「謝罪」ではなく「救済」だ。
2. 設計思想:階層型ストレージ・戦略
我々が実装すべきは、以下の階層構造を持つ保存プロセスである。
1. Primary Target (主保存先): 業務ルールに基づいたネットワークパス(DWG/DXF)。
2. Secondary Target (緊急退避先): ネットワークに依存しないローカル環境(デスクトップやTempフォルダ)。
エラーが発生した際、プログラムの制御を即座に「救済モード」へと切り替え、タイムスタンプを付与したユニークなファイル名でローカルに書き出す。これがフォールバック(Fallback)の概念だ。
3. 実装:プロダクション・レディな堅牢コード
以下に、実務に投入可能なレベルのコードを示す。単なるコピペではなく、なぜこのように書くのか、その意図を読み解いてほしい。
Option Explicit
''' <summary>
''' 堅牢な保存処理を実行する。
''' ネットワーク保存に失敗した場合、自動的にローカルの緊急退避フォルダへ保存を試みる。
''' </summary>
''' <param name="targetPath">保存したい本来のフルパス</param>
Public Sub RobustSaveAs(ByVal targetPath As String)
Dim doc As AcadDocument
Set doc = ThisDrawing ' ActiveDocumentを直接使わず、オブジェクト変数に格納するのが鉄則
Dim fso As Object
Set fso = CreateObject("Scripting.FileSystemObject")
' 1. 本来の保存を試行
On Error GoTo ErrorHandler_Primary
doc.SaveAs targetPath
Debug.Print "Primary Save Success: " & targetPath
Exit Sub
ErrorHandler_Primary:
' 2. 失敗した場合、エラー内容をログ出力(デバッグ用)
Debug.Print "Primary Save Failed. Error: " & Err.Description
' 3. フォールバック(緊急退避)フェーズへ移行
ExecuteEmergencyFallback doc, targetPath, fso
Exit Sub
End Sub
''' <summary>
''' 緊急退避用の保存ロジック
''' </summary>
Private Sub ExecuteEmergencyFallback(ByRef doc As AcadDocument, ByVal originalPath As String, ByRef fso As Object)
On Error GoTo ErrorHandler_Fatal
Dim localFolder As String
Dim timestamp As String
Dim fileName As String
Dim fallbackPath As String
Dim baseName As String
' A. 退避先フォルダの決定(デスクトップ上の Emergency_Backup フォルダ)
' 環境変数を使用して、ユーザーに依存しないパスを取得する
localFolder = CreateObject("WScript.Shell").SpecialFolders("Desktop") & "\AutoCAD_Emergency_Backup"
' フォルダが存在しなければ作成
If Not fso.FolderExists(localFolder) Then
fso.CreateFolder localFolder
End If
' B. タイムスタンプの生成 (例: 20231027_143005)
timestamp = Format(Now, "yyyymmdd_hhmmss")
' C. ファイル名の構成
' 元のパスから拡張子を除いたファイル名を取得
baseName = fso.GetBaseName(originalPath)
fileName = baseName & "_RESCUED_" & timestamp & ".dwg"
fallbackPath = localFolder & "\" & fileName
' D. ローカル保存の実行
doc.SaveAs fallbackPath
' E. ユーザーへの通知(重要:ユーザーに「救済されたこと」を明確に伝える)
MsgBox "【警告】ネットワーク接続エラーにより、本来の保存に失敗しました。" & vbCrLf & vbCrLf & _
"データを保護するため、以下の場所に緊急退避保存を行いました:" & vbCrLf & _
fallbackPath, vbExclamation, "データ救済完了"
Exit Sub
ErrorHandler_Fatal:
' フォールバックすら失敗した場合(PCのディスクフル等)
MsgBox "致命的なエラー: 緊急退避保存にも失敗しました。手動でのコピーを試みてください。" & vbCrLf & _
"Error: " & Err.Description, vbCritical, "システム崩壊"
End Sub
4. アーキテクトの視点:このコードの「重み」
このコードが、なぜ「単なるエラー回避」を超えているのか。その設計の要点を解説する。
① オブジェクトの明示的参照
`ActiveDocument` を直接メソッドに渡すのではなく、`Set doc = ThisDrawing` とオブジェクト変数に格納している。これは、エラーハンドリング中にドキュメントの状態が不安定になった際、参照の不一致を防ぎ、デバッグの確実性を高めるための作法だ。
② 環境変数を利用したパス解決
`”C:\Users\UserName\Desktop”` とハードコーディングするのは素人のすることだ。`WScript.Shell` を介して `SpecialFolders(“Desktop”)` を取得することで、ユーザー名が異なるどのPC環境でも、あるいはプロファイルが特殊な環境でも、確実に動作する。
③ タイムスタンプによる「衝突」の回避
ネットワーク切断は、一度起きると断続的に発生する。同じファイル名で何度も保存を試みると、ファイルロックや上書きの競合が発生する。`Format(Now, “yyyymmdd_hhmmss”)` を用いることで、保存のたびにユニークなファイルが生成され、確実に「過去の試行」を履歴として残せる。
④ ユーザー体験(UX)の設計
エラーが起きたとき、ユーザーが最も知りたいのは「私のデータは無事か?」だ。
`MsgBox` のメッセージには、「何が起きたか(ネットワークエラー)」「どう対処したか(ローカル保存)」「どこにあるか(フルパス)」の3点を必ず含める。これにより、ユーザーの不安を解消し、次のアクション(ローカルからサーバーへ手動で戻す等)を促すことができる。
5. さらなる高みへ:データベース連携への応用
もし君が、図面の属性情報をSQL ServerやAccessなどのデータベースと連携させているなら、このフォールバックはさらに複雑になる。
図面(DWG)の保存に失敗した際、同時に発行しようとしていた「属性情報のDB更新トランザクション」もロールバックしなければならない。「図面はローカルにあるが、DBには反映されていない」という状態は、データの整合性が崩れた「不整合状態」だ。
実務レベルでは、このローカル保存に成功した際、その「退避完了フラグ」を一時的なログファイル(JSONやCSV)として出力しておき、ネットワーク復旧後に一括で同期する仕組みまで設計するのが、真のチーフアーキテクトの仕事だ。
結言
「動くコード」を書くのは簡単だ。しかし、「止まらない、そしてデータを失わないコード」を書くには、最悪の事態を想像する力が必要である。
ネットワークは切れる。サーバーは落ちる。ディスクは満杯になる。
それらの「不都合な真実」を設計に組み込んだとき、君の書いたツールは、単なるスクリプトから「信頼されるシステム」へと昇華する。
設計に妥協はない。データに妥協は許されない。
