差分抽出の深淵:DWG Compare結果の自動保存とオブジェクト・ライフサイクルの極限制御
設計プロセスにおける「新旧図面の比較」は、単なる作業ではない。それは、設計変更の整合性を担保するための、極めてクリティカルな検証フェーズである。
多くのエンジニアは、AutoCADの「図面比較(DWG Compare)」機能の利便性を享受している。しかし、これを「自動化」しようとした瞬間、彼らは壁にぶつかる。なぜなら、`COMPARE` コマンドは、標準的な `ActiveDocument` オブジェクトのプロパティ操作では制御できない、「一時的な比較セッション」という特殊な状態を生成するからだ。
本稿では、単なるコマンドの実行手順を教えるのではない。コマンドの非同期性、メモリへの負荷、そしてWindows APIを用いたプロセス制御。これらを統合し、企業レベルの自動化システムに耐えうる「差分図面自動保存エンジン」を構築するための知見を伝授する。
1. アーキテクチャの設計思想:非同期性の克服
`SendCommand` メソッドは、VBAにおける自動化の強力な武器だが、最大の弱点は「非同期(Asynchronous)である」ことだ。VBA側でコマンドを投げた直後、AutoCAD内部ではまだ比較処理が走っている。ここで安易に `SaveAs` を実行すれば、確実にランタイムエラー、あるいは空のファイルが生成される。
我々が目指すべきは、コマンドの完了を「待機」する制御ロジックである。
制御戦略
1. Command Injection: `COMPARE` コマンドに比較対象のパスを引数として渡す。
2. State Polling: `DoEvents` と微小なウェイトを組み合わせ、AutoCADのコマンドライン状態を監視する。
3. Transient State Capture: 比較結果の特殊な状態において、強制的に `SAVEAS` を発行する。
2. 実装:極限の堅牢性を備えた比較エンジン
以下に、実務でそのまま利用可能なレベルのコード構造を示す。ここでは、メモリリークを防止するための明示的なオブジェクト解放と、Windows APIによるプロセス制御を組み込んでいる。
Option Explicit
' Windows API: プロセスの待機制御用
If VBA7 Then
Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As LongPtr)
Else
Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
End If
' 定数定義
Private Const CMD_COMPARE As String = "COMPARE"
Private Const CMD_SAVEAS As String = "SAVEAS"
''' <summary>
''' 2つの図面を比較し、その結果を別名で保存する
''' </summary>
''' <param name="baseDocPath">基準となる図面パス</param>
''' <param name="compareDocPath">比較対象の図面パス</param>
''' <param name="outputPath">保存先パス</param>
Public Sub AutomateDWGCompare(ByVal baseDocPath As String, _
ByVal compareDocPath As String, _
ByVal outputPath As String)
Dim acadApp As Object
Dim docBase As Object
Dim targetPath As String
On Error GoTo ErrorHandler
' AutoCADアプリケーションへの参照取得
Set acadApp = ThisDrawing.Application
' 1. 基準図面を明示的に開く(既に開いている場合はそれを使用)
' ※メモリ最適化のため、既存のドキュメントとの競合を避ける設計が必要
Set docBase = acadApp.Documents.Open(baseDocPath)
' 2. COMPAREコマンドの実行
' コマンドライン経由で比較対象のパスを渡す。
' パスにスペースが含まれる場合に備え、ダブルクォーテーションで囲む。
docBase.SendCommand CMD_COMPARE & " """ & compareDocPath & """ "
' 3. 【極めて重要】比較処理の完了待機
' 単なるSleepではなく、コマンドラインの状態を確認するロジックが理想だが、
' 実務上は十分なウェイトとDoEventsの組み合わせが最も安定する。
Debug.Print "Comparing documents... Please wait."
WaitCommandCompletion acadApp, 5000 ' 最大5秒待機
' 4. 比較結果の保存
' COMPARE実行後のドキュメントは特殊な状態にあるため、
' SendCommandを使用して強制的にSAVEASをトリガーする。
docBase.SendCommand CMD_SAVEAS & " """ & outputPath & """ "
' 保存完了を待機
Sleep 2000
Debug.Print "Success: Comparison result saved to " & outputPath
CleanUp:
' 5. オブジェクトのライフサイクル管理(メモリリーク防止)
' 明示的にNothingを代入し、参照カウントをゼロにする。
' これを行わないと、AutoCADプロセスがバックグラウンドで残り続ける。
On Error Resume Next
If Not docBase Is Nothing Then
' 比較セッションを閉じるために、保存せずに閉じる必要がある場合もある
' docBase.Close False
Set docBase = Nothing
End If
Set acadApp = Nothing
Exit Sub
ErrorHandler:
MsgBox "Critical Error: " & Err.Description, vbCritical, "Architecture Failure"
Resume CleanUp
End Sub
''' <summary>
''' AutoCADのコマンド実行を監視するためのポーリング関数
''' </summary>
Private Sub WaitCommandCompletion(ByRef acadApp As Object, ByVal maxWaitMs As Long)
Dim startTime As Double
startTime = Timer
Do While Timer < startTime + (maxWaitMs / 1000)
DoEvents ' OSに制御を戻し、AutoCADの内部処理を進行させる
Sleep 500 ' CPU負荷を抑えるための微小待機
' 本来はここで acadApp.GetVariable("CMDACTIVE") 等を監視すべき
' だが、COMPAREコマンドの特殊性を考慮し、時間ベースの制御を優先
Loop
End Sub
3. 技術的深掘り:なぜ「Nothing」が必要なのか
シニアエンジニアが直面する最大の敵は、「ゾンビプロセス」である。
AutoCAD VBAにおいて、`Set doc = Nothing` を怠ることは、単なる行儀の悪さではない。それは、COM(Component Object Model)の参照カウントを不適切に操作し、メモリ上に「解放不可能なドキュメント・インスタンス」を蓄積させる行為だ。
特に、`COMPARE` コマンドのように、AutoCADの内部エンジンが一時的なデータベース・トランザクションを開始する操作の直後は、オブジェクトのライフサイクルが極めて不安定になる。コード内で明示的に `Set = Nothing` を行い、さらに `DoEvents` を挟むことで、Windowsのメッセージキューを適切に処理させ、AutoCADが「コマンド完了」と「オブジェクト解放」の整合性を取れる時間を確保しなければならない。
パフォーマンスの重み
大規模なDWGファイルを扱う場合、比較処理は膨大なメモリを消費する。
- メモリフラグメンテーションの回避: 大量の図面をループで処理する場合、一つのプロセス内で全てを完結させず、必要に応じてAutoCAD自体の再起動、あるいは別プロセス(`CreateObject` による別インスタンス)での実行を検討すべきである。
- APIの選択: `ThisDrawing`(現在の図面)を使うのか、`Documents.Open`(新しい図面)を使うのか。前者は現在のコンテキストに縛られ、後者はリソース管理の自由度が高い。自動化システムを構築するなら、後者の「制御可能なインスタンス」を常に選択せよ。
4. 結論:システム管理者への提言
この自動化プロセスを社内システムとして展開する場合、単にVBAを配布するだけでは不十分だ。
1. 例外処理の標準化: `On Error GoTo` だけでは、ネットワーク切断やファイルロックによるエラーを捕捉しきれない。ファイルシステムの状態(`FileSystemObject`)を確認する前処理を組み込むこと。
2. ログの外部出力: AutoCAD内部の `Debug.Print` は、自動実行時には無意味だ。Windows APIを用いたテキストファイルへのログ書き出しを実装し、実行結果のトレーサビリティを確保せよ。
3. バージョン互換性: `COMPARE` コマンドはAutoCADのバージョンによって挙動が微妙に異なる。レガシー環境を保守する場合、コマンドラインのプロンプト待ち受けパターンを厳密にテストすること。
自動化とは、単に作業を減らすことではない。「人間が介在することによる不確実性を、数学的な決定論へと変換するプロセス」である。この知見を、貴殿のアーキテクチャに組み込んでほしい。
