【テクニカル・上級編】【上級プロフェッショナル向け】ネットワーク切断時の「トランザクション保存」:ローカルへの一時書き出しと復旧同期システム – AutoCAD VBA解析バイブル

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

データの生存権を確保せよ:AutoCAD自動化における「ローカル・シャドウ・トランザクション」の実装

ネットワーク経由でのDWG管理。それは、エンジニアリングにおける「常に走っている綱渡り」に等しい。

多くの開発者は、`Document.Save` や `Document.SaveAs` を呼び出すだけで満足している。しかし、数GBに及ぶ大規模なアセンブリや、複雑なXref(外部参照)が絡み合うプロジェクトにおいて、保存の瞬間にSMBプロトコルの瞬断、あるいはサーバーのI/Oスロットリングが発生した際、何が起きるかを知っているだろうか?

ファイルはゼロバイトになり、DWGのヘッダーは破損し、ユーザーの数時間の作業は塵に帰す。これは「エラー」ではない。設計の「欠陥」だ。

本稿では、ネットワークの不安定性を前提とした、「ローカル・シャドウ・トランザクション(Local Shadow Transaction)」システムの設計思想と、その実装コードを詳述する。これは単なるバックアップではない。データの生存権を、不安定なネットワークから奪い取り、ローカルの制御下に置くためのアーキテクチャである。

1. アーキテクチャの根幹:不信の原則

プロフェッショナルなシステム設計において、我々は「ネットワークは常に切断されるもの」と定義しなければならない。

通常の保存フローは以下の通りだ:
`Memory (AutoCAD) $\rightarrow$ Network Interface $\rightarrow$ Server Storage`

我々が構築すべき「シャドウ・トランザクション」は、ここに「Local Interceptor」を割り込ませる。

1. Pre-flight Check: 書き込み前に、ターゲットパスの到達性をWindows APIレベルで検証する。
2. Shadow Write: ネットワークが不安定、あるいは書き込みに遅延が検知された場合、即座にローカルの隠しディレクトリ(`%LOCALAPPDATA%`)へ、シリアル番号とタイムスタンプを付与した一時ファイルとして書き出す。
3. Reconciliation (同期): ネットワーク復旧を検知した際、サーバー上のファイルとローカルのシャドウファイルを比較し、コンフリクトを解決するためのステージングを行う。

2. Windows APIによる低レイヤーの接続検証

VBAの `Dir()` 関数や `FileSystemObject` は、ネットワークの瞬断を検知するにはあまりに鈍重だ。OSのカーネルに近いレイヤー、すなわち `Kernel32.dll` の力を借りる必要がある。

ここでは、ネットワークパスの可用性を高速に判定するためのアプローチを示す。

' --- Module: Win32API_Internal ---
Option Explicit

If VBA7 Then

' 64bit AutoCAD環境への対応 Declare PtrSafe Function GetFileAttributes Lib "kernel32" Alias "GetFileAttributesA" (ByVal lpFileName As String) As Long

Else

Declare Function GetFileAttributes Lib "kernel32" Alias "GetFileAttributesA" (ByVal lpFileName As String) As Long

End If

Public Const INVALID_FILE_ATTRIBUTES As Long = -1 ''' <summary> ''' ネットワークパスの生存性を低レイヤーで検証する ''' </summary> Public Function IsPathReachable(ByVal strPath As String) As Boolean Dim attributes As Long ' ファイルそのものではなく、ディレクトリの属性を確認することで、 ' ネットワークパスへの到達性を高速に判定する attributes = GetFileAttributes(strPath) If attributes = INVALID_FILE_ATTRIBUTES Then IsPathReachable = False Else IsPathReachable = True End If End Function

3. 実装:シャドウ・トランザクション・エンジン

次に、核心となる保存ロジックだ。ここで重要なのは、オブジェクトのライフサイクル管理である。AutoCADの `Document` オブジェクトは、保存失敗時に不安定な状態(ゾンビ状態)になりやすい。エラーハンドリングの中で、明示的なメモリ解放とオブジェクトの再割り当てを行う設計が不可欠だ。

' --- Module: TransactionManager ---
Option Explicit

Private Type ShadowMetadata
    OriginalPath As String
    LocalPath As String
    Timestamp As String
    IsRecoverable As Boolean
End Type

''' <summary>
''' 高度なトランザクション保存を実行する
''' </summary>
Public Sub ExecuteSafeSave(ByRef acadDoc As Object)
    Dim targetPath As String
    Dim localVault As String
    Dim shadowFile As String
    Dim meta As ShadowMetadata
    
    On Error GoTo ErrorHandler

    targetPath = acadDoc.FullName
    ' ユーザーのローカルアプリデータディレクトリを取得
    localVault = Environ("LOCALAPPDATA") & "\AutoCAD_Shadow_Vault\"
    
    ' ボルト(隠しフォルダ)の存在確認と作成
    If Dir(localVault, vbDirectory) = "" Then
        MkDir localVault
    End If

    ' 1. Pre-flight Check: ネットワークの到達性確認
    If Not IsPathReachable(Left(targetPath, InStrRev(targetPath, "\"))) Then
        Debug.Print "Network Unreachable. Initiating Shadow Write..."
        GoTo InitiateShadowWrite
    End If

    ' 2. 通常の保存試行
    acadDoc.Save
    Exit Sub

InitiateShadowWrite:
    ' 3. シャドウ・ライト (ローカルへの退避)
    ' ファイル名にシリアルとタイムスタンプを付与し、衝突を回避
    meta.OriginalPath = targetPath
    meta.Timestamp = Format(Now, "yyyymmdd_hhmmss")
    shadowFile = localVault & "SHADOW_" & meta.Timestamp & "_" & _
                 Replace(Mid(targetPath, InStrRev(targetPath, "\") + 1), ".dwg", "") & ".dwg"
    
    meta.LocalPath = shadowFile
    
    ' AutoCADのSaveAsを用いてローカルへ強制書き出し
    ' ここでのエラーは、ローカルディスクの容量不足等を意味する
    acadDoc.SaveAs shadowFile
    
    ' メタデータ(どこから来たファイルか)をログとして保存
    WriteShadowLog meta
    
    MsgBox "警告: ネットワーク切断を検知しました。作業内容はローカルに一時退避されました。" & vbCrLf & _
           "復旧後に自動同期を開始します。", vbExclamation, "Shadow Transaction Active"
    Exit Sub

ErrorHandler:
    ' 4. メモリ管理とオブジェクトのクリーンアップ
    ' エラー発生時、オブジェクトが破損している可能性があるため、
    ' 参照を切り離し、システムに制御を戻す
    Debug.Print "Critical Error in Save Transaction: " & Err.Description
    Set acadDoc = Nothing 
    ' 注意: 実運用では、呼び出し元で再取得するロジックが必要
    Err.Raise Err.Number, "TransactionManager", "Save transaction failed. Memory integrity compromised."
End Sub

Private Sub WriteShadowLog(ByRef meta As ShadowMetadata)
    Dim logFile As String
    Dim fso As Object
    Set fso = CreateObject("Scripting.FileSystemObject")
    
    logFile = Environ("LOCALAPPDATA") & "\AutoCAD_Shadow_Vault\sync_manifest.log"
    
    ' 追記モードでメタデータを記録
    With fso.OpenTextFile(logFile, 8, True)
        .WriteLine meta.Timestamp & "|" & meta.OriginalPath & "|" & meta.LocalPath
        .Close
    End With
    Set fso = Nothing
End Sub

4. 復旧同期(Reconciliation)の設計思想

シャドウ・ライトが成功した後、システムは「待機状態」に入る。ネットワークが復旧した際、あるいはユーザーが再起動した際、以下のロジックを走らせる。

コンフリクト解決のアルゴリズム

1. Manifestの走査: `sync_manifest.log` を読み込み、未同期のシャドウファイルリストを作成する。
2. 比較(Diffing):

  • サーバー上の `OriginalPath` の最終更新日時。
  • シャドウファイルの `Timestamp`。

3. 判定:

  • `Shadow.Timestamp > Server.LastModified` $\rightarrow$ 衝突(Conflict)。
  • この場合、自動上書きは「禁忌」である。サーバー側を `.old` としてリネームし、シャドウファイルを本来の名称で配置する、あるいはユーザーに「どちらを優先するか」を問う対話型ダイアログを提示する。

5. 伝説的アーキテクトからの助言

このシステムを実装するにあたって、シニアエンジニアが肝に銘じるべきことが3つある。

1. 「保存」はアトミック(不可分)ではない:
AutoCADの `SaveAs` は、ファイルへの書き込み、ヘッダーの更新、インデックスの再構築という複数のステップを踏む。この途中でプロセスが死ぬことを常に想定せよ。ローカルへの書き出しを「メインの保存」とみなすくらいの堅牢性が必要だ。

2. メモリリークへの執着:
VBAはガベージコレクションが極めて脆弱だ。特にエラーハンドリング内での `Set obj = Nothing` を怠ると、AutoCADのプロセス自体がメモリを食いつぶし、最終的に「Out of Memory」でクラッシュする。これはファイル破損の引き金となる。

3. レガシーへの敬意:
古いAutoCADバージョンや、不安定なSMB 1.0環境が残る現場では、Windows APIの挙動が微妙に異なる。常に `PtrSafe` による64bit対応と、プラットフォームの差異を吸収する抽象化レイヤーを維持せよ。

エンジニアリングとは、正常な状態を作ることではない。異常な状態において、いかに優雅に、かつ確実にデータを守り抜くか、その一点に集約される。

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