【テクニカル・上級編】【初心者向け】図面を開く際の「パスワード保護」有無の事前判定と、実行時エラーのスマートな回避方法 – AutoCAD VBA解析バイブル

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

「沈黙の失敗」を制御せよ:パスワード保護図面によるバッチ処理の崩壊を防ぐ極限のアーキテクチャ

バッチ処理を走らせた翌朝、エンジニアが目にするのが「1,000ファイル中、3ファイル目で停止したAutoCAD」という無残な光景である。

多くの中級者は、`On Error Resume Next` を乱用してその場を凌ごうとする。しかし、それは技術的な敗北を意味する。パスワード保護されたDWGファイルに直面したとき、AutoCADが投げかけるのは単なる「エラー」ではない。ユーザーの介入を強要する「モーダルダイアログ」という名の、自動化プロセスに対する致命的な割り込み(Interrupt)である。

本稿では、単なるエラー回避のテクニックを超え、オブジェクトのライフサイクル、COM参照カウント、そしてシステムとしての堅牢性を担保するための「極限の設計思想」を伝授する。

1. 根本的な問題:なぜ `On Error` だけでは足りないのか

AutoCADのCOMインターフェースにおいて、`Documents.Open` メソッドは、ファイルがパスワード保護されている場合、AutoCAD本体のダイアログ制御権を奪い取る。

VBAの標準的なエラーハンドリングは、プロシージャ内の実行フローを制御するものに過ぎない。しかし、モーダルダイアログが表示された瞬間、制御はAutoCADのメインスレッドへと移り、VBAの実行ループは「待機状態」ではなく「凍結状態」に陥る。これが、バッチ処理が停止する真のメカニズムだ。

我々アーキテクトが目指すべきは、「ダイアログを発生させない、あるいは発生した瞬間にそのプロセスを切り離して次へ進む」設計である。

2. 実装戦略:プロアクティブな判定と堅牢なハンドリング

実用的な解法として、以下の3層構造による防御策を講じる。

1. FileSystemObject (FSO) による事前検証層: ファイルの存在、アクセス権、およびファイルサイズによる異常検知。
2. Try-Catch パターンによる実行制御層: `Documents.Open` を隔離されたプロシージャで実行し、エラーを「検知」するのではなく「管理」する。
3. メモリ・ハイジーン(衛生)層: エラー発生時、あるいは処理完了時に、COMオブジェクトの参照カウントを確実にゼロにし、ゾンビプロセス(メモリ上に残存するAutoCAD)を排除する。

実装例:極限のバッチ・プロセッサ

以下のコードは、シニアエンジニアが現場で即座に展開できるレベルの堅牢性を備えた実装パターンである。

Option Explicit

' ==============================================================================
' Module: BatchProcessor
' Description: パスワード保護図面を回避し、メモリリークを防ぐ高度なバッチ処理
' Architect: Legendary Chief Architect
' ==============================================================================

Implements IBatchProcessor ' 必要に応じてインターフェースを定義

Private Sub ProcessAllFiles(ByVal targetFolder As String)
    Dim fso As Object
    Dim folder As Object
    Dim file As Object
    Dim acadApp As Object
    Dim doc As Object
    Dim logPath As String
    
    ' 初期化: FileSystemObjectの生成
    Set fso = CreateObject("Scripting.FileSystemObject")
    logPath = targetFolder & "\process_log.txt"
    
    ' AutoCADアプリケーションへの接続
    On Error Resume Next
    Set acadApp = GetObject(, "AutoCAD.Application")
    If acadApp Is Nothing Then
        Set acadApp = CreateObject("AutoCAD.Application")
    End If
    On Error GoTo 0
    
    If Not fso.FolderExists(targetFolder) Then
        MsgBox "Target folder not found.", vbCritical
        Exit Sub
    End If

    Set folder = fso.GetFolder(targetFolder)
    
    ' メインループ
    For Each file In folder.Files
        ' DWG/DXFファイルのみを対象とする
        If LCase(fso.GetExtensionName(file.Name)) = "dwg" Or _
           LCase(fso.GetExtensionName(file.Name)) = "dxf" Then
            
            Debug.Print "Processing: " & file.Name
            
            ' 個別のファイル処理を隔離された関数で実行
            ' これにより、エラー発生時のスタックを最小限に抑える
            Call SafeOpenAndProcess(acadApp, file.Path, logPath)
            
        End If
    Next file

    ' クリーンアップ
    Set fso = Nothing
    Debug.Print "Batch Process Completed."
End Sub

''' <summary>
''' 図面を安全に開き、エラー(パスワード等)が発生した場合はログに記録して即座に復帰する
''' </summary>
Private Sub SafeOpenAndProcess(ByRef acadApp As Object, ByVal filePath As String, ByVal logPath As String)
    Dim doc As Object
    Dim errNum As Long
    Dim errDesc As String

    On Error GoTo ErrorHandler

    ' --- 実行フェーズ ---
    ' Documents.Open はパスワード保護時にダイアログを出す。
    ' ここでのエラーハンドリングが生命線となる。
    Set doc = acadApp.Documents.Open(filePath)
    
    ' --- ここに図面処理のロジックを記述 ---
    ' 例: doc.ModelSpace.AddLine(...)
    ' ----------------------------------

    ' 処理完了後のクローズ(保存しない場合は False)
    doc.Close False
    
    ' 明示的なオブジェクト解放 (COM参照カウントの管理)
    Set doc = Nothing
    Exit Sub

ErrorHandler:
    errNum = Err.Number
    errDesc = Err.Description
    
    ' エラーログの記録
    Call WriteLog(logPath, "ERROR", filePath & " | Code: " & errNum & " | Msg: " & errDesc)
    
    ' 重要: エラー発生時、docオブジェクトが中途半端に生成されている可能性があるため
    ' 確実にNothingを代入し、メモリリークを防止する
    Set doc = Nothing
    
    ' ここで Err.Clear を行い、次のループへの影響を遮断する
    Err.Clear
End Sub

''' <summary>
''' ログ記録プロシージャ(I/O負荷を考慮し、必要最小限の実装とする)
''' </summary>
Private Sub WriteLog(ByVal logPath As String, ByVal level As String, ByVal message As String)
    Dim fso As Object
    Dim ts As Object
    Set fso = CreateObject("Scripting.FileSystemObject")
    
    ' 追記モード(8)で開く
    Set ts = fso.OpenTextFile(logPath, 8, True)
    ts.WriteLine Now & " [" & level & "] " & message
    ts.Close
    
    Set ts = Nothing
    Set fso = Nothing
End Sub

3. アーキテクトの深淵:メモリとオブジェクトのライフサイクル

このコードが単なる「初心者向けコード」と一線を画す理由は、オブジェクトのライフサイクル管理にある。

COM参照カウントの重要性

VBAにおいて、`Set doc = Nothing` は単なる「おまじない」ではない。AutoCADのような巨大なアプリケーションを操作する場合、変数にオブジェクトが残っている限り、そのオブジェクトの参照カウント(Reference Count)は減少しない。

もしエラー発生時に `Set doc = Nothing` を怠れば、エラーが起きた図面がメモリ上に「幽霊(Ghost Object)」として残り続ける。これが積み重なると、AutoCADのメモリ使用量は指数関数的に増大し、最終的にはシステム全体がスワップ(仮想メモリへの書き出し)を起こして、処理速度が壊滅的に低下する。

Windows APIによるプロセスの監視(レガシー環境の保守)

さらに高度な要件として、ダイアログによる「停止」を検知したい場合は、Windows APIの `FindWindow` や `EnumWindows` を用いて、AutoCADのメインウィンドウの子要素として「Password」や「Open」といった文字列を持つダイアログが存在するかを監視する手法もある。しかし、これは副作用が大きいため、まずは上記のような「隔離された実行(Encapsulation)」によるエラーハンドリングを優先すべきだ。

4. 総括:エンジニアリングの真髄

パスワード保護図面への対処とは、単にエラーを無視することではない。

1. 予測可能性(Predictability): エラーが起きても、システム全体が停止しないことを保証する。
2. 観測可能性(Observability): 何が原因でスキップされたのか、ログによって事後検証を可能にする。
3. 回復性(Resilience): メモリ管理を徹底し、長時間稼働に耐えうる状態を維持する。

これらを満たして初めて、君の書いたコードは「スクリプト」から「システム」へと昇華する。自動化の真の価値は、例外的な事象(パスワード保護、ファイル破損、ネットワーク断)に直面したときの、その振る舞いの美しさに宿るのだ。

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