【実務・中級編】【実務中級者向け】「SaveAs」失敗時の代替保存先確保:ネットワーク切断時にローカルの「緊急退避フォルダ」へ自動保存する – AutoCAD VBA解析バイブル

スポンサーリンク
スポンサーリンク

【実務の極致】データ消失の恐怖を設計でねじ伏せる: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)として出力しておき、ネットワーク復旧後に一括で同期する仕組みまで設計するのが、真のチーフアーキテクトの仕事だ。

結言

「動くコード」を書くのは簡単だ。しかし、「止まらない、そしてデータを失わないコード」を書くには、最悪の事態を想像する力が必要である。

ネットワークは切れる。サーバーは落ちる。ディスクは満杯になる。
それらの「不都合な真実」を設計に組み込んだとき、君の書いたツールは、単なるスクリプトから「信頼されるシステム」へと昇華する。

設計に妥協はない。データに妥協は許されない。

タイトルとURLをコピーしました