【実務・中級編】【実務中級者向け】「図面比較(DWG Compare)」結果を自動保存:VBAによる2つのバージョン間の差分抽出自動化 – AutoCAD VBA解析バイブル

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

凡人の「差分比較」を卒業せよ:DWG Compareを自動化するアーキテクチャ設計論

「新旧の図面を比較して、変更箇所を特定し、結果を保存する」

実務において、このタスクを依頼されることは多い。しかし、多くの開発者が陥る致命的なミスがある。それは、「VBAで全てのエンティティをループで回し、座標やレイヤーを一つずつ比較しようとする」ことだ。

もし君がそんなコードを書こうとしているなら、今すぐその手を止めろ。それは、砂漠で一粒ずつ砂を数えるような、無意味で、極めてパフォーマンスの低い、そして脆弱な設計だ。

AutoCADには、強力な比較エンジンである`DWGCOMPARE`コマンドが既に存在する。我々エンジニアの仕事は、そのエンジンを「どう制御し、どう結果を業務プロセスに組み込むか」というオーケストレーション(指揮)にある。

本稿では、単なるコードの書き方ではなく、業務で耐えうる「堅牢な自動比較システム」の設計思想を伝授する。

1. なぜ「エンティティ比較ループ」はゴミなのか

まず、設計の前提となる「失敗のパターン」を叩き込んでおく。

多くの初級者は、`For Each obj In ModelSpace` を使い、新旧図面のオブジェクトを比較しようとする。しかし、このアプローチには以下の致命的な欠陥がある。

1. IDの不一致: 図面を一度でも保存し直したり、別のソフトを経由したりすると、オブジェクトのハンドル(Handle)やIDは変わる。
2. 計算量の爆発: 数万個のオブジェクトを持つ図面で、二重ループ(O(n²))を回せば、AutoCADはフリーズしたように沈黙する。
3. 比較精度の限界: 線分(Line)がポリライン(Polyline)に変換されていた場合、属性比較だけでは「変更」を正しく検知できない。

プロフェッショナルの解:
AutoCADのネイティブコマンドである`DWGCOMPARE`を`SendCommand`経由で叩く。これにより、AutoCAD内部の高度な幾何学解析エンジンをそのまま利用し、計算負荷と精度問題を同時に解決する。

2. 堅牢な設計:3つのレイヤー

実務ツールを作るなら、コードを「ファイル操作」「コマンド実行」「エラーハンドリング」の3層に分離して設計すべきだ。

① ファイル管理層 (File Management Layer)

パスの妥当性、ファイルの存在確認、書き込み権限のチェックを行う。ファイルが存在しない、あるいは他のユーザーがロックしている状態でコマンドを投げれば、即座にシステムはクラッシュする。

② コマンド制御層 (Command Control Layer)

`SendCommand`は強力だが、非同期的に動作する。コマンドが完了する前に次の処理へ進むと、予期せぬ挙動を引き起こす。コマンドの実行タイミングと、図面状態の同期を制御する必要がある。

③ ライフサイクル管理層 (Lifecycle Management Layer)

比較が終わった後、比較結果を表示したままにするのか、それとも特定の名前で「差分図面」として保存するのか。図面のOpen/Closeのライフサイクルを厳格に管理し、メモリリークやゾンビプロセスを防ぐ。

3. 実装:プロダクション・クオリティの比較エンジン

以下に、実務でそのまま使えるレベルのコード構造を示す。これは、単なる比較ではなく、「新旧図面を比較し、結果を特定の名前で保存する」というワークフローを完結させるための設計だ。

'**
' Module: AutomationEngine
' Description: 高度なDWG比較および結果保存プロセス
' Author: Chief Architect
'**

Option Explicit

' ファイル操作のための参照設定: Microsoft Scripting Runtime
' (ツール -> 参照設定 から追加すること)

Public Sub ExecuteAutomatedComparison()
    Dim fso As New FileSystemObject
    Dim oldDocPath As String
    Dim newDocPath As String
    Dim resultDocPath As String
    Dim targetFolder As String
    
    ' --- 1. 設定セクション (本来はExcelやDBから取得すべき) ---
    oldDocPath = "C:\Project\Drawing_V1.dwg"
    newDocPath = "C:\Project\Drawing_V2.dwg"
    targetFolder = "C:\Project\Comparison_Results\"
    
    ' 結果の保存先パスを生成 (例: Drawing_V1_vs_V2_Diff.dwg)
    resultDocPath = targetFolder & "Diff_" & _
                    fso.GetBaseName(oldDocPath) & "_vs_" & _
                    fso.GetBaseName(newDocPath) & ".dwg"

    ' --- 2. 事前バリデーション (ガード句) ---
    On Error GoTo ErrorHandler
    
    If Not fso.FileExists(oldDocPath) Then Err.Raise 53, , "旧図面が見つかりません: " & oldDocPath
    If Not fso.FileExists(newDocPath) Then Err.Raise 53, , "新図面が見つかりません: " & newDocPath
    If Not fso.FolderExists(targetFolder) Then fso.CreateFolder (targetFolder)

    ' --- 3. 比較プロセスの実行 ---
    Debug.Print "Comparison Started: " & Now
    
    ' AutoCADのメインプロセスへ制御を移す
    PerformCompare oldDocPath, newDocPath
    
    ' ※注意: DWGCOMPAREコマンド自体は「現在の図面」に「別の図面」を重ねる挙動をする。
    ' 比較結果を「ファイル」として残すには、比較モードの後に
    ' 変更箇所を抽出する、あるいは比較状態の図面をSaveAsする戦略をとる。
    
    ' ここでは、比較を実行した後の図面を「差分結果」として保存するフローを想定
    ThisDrawing.SaveAs resultDocPath
    
    MsgBox "比較プロセスが正常に完了しました。" & vbCrLf & "保存先: " & resultDocPath, vbInformation
    
ExitSub:
    Set fso = Nothing
    Exit Sub

ErrorHandler:
    MsgBox "致命的なエラーが発生しました。" & vbCrLf & _
           "Error No: " & Err.Number & vbCrLf & _
           "Description: " & Err.Description, vbCritical
    Resume ExitSub
End Sub

''' <summary>
''' AutoCADのネイティブコマンドを使用して比較を実行する
''' </summary>
Private Sub PerformCompare(ByVal oldPath As String, ByVal newPath As String)
    ' DWGCOMPAREコマンドは、現在開いている図面に対して、
    ' 比較対象のパスを引数として渡すことで動作する。
    
    ' 1. まず、比較のベースとなる「旧図面」を開く
    Dim docOld As AcadDocument
    Set docOld = Documents.Open(oldPath)
    
    ' 2. 比較コマンドの実行
    ' SendCommandは文字列をコマンドラインに直接打ち込む。
    ' 引数として比較対象(newPath)を渡す。
    ' 文末のスペースはEnterキーの代わり。
    docOld.SendCommand "DWGCOMPARE " & Chr(34) & newPath & Chr(34) & " "
    
    ' 3. コマンドが完了するまで待機するロジックが必要な場合があるが、
    ' DWGCOMPAREはUI制御が強いため、基本的にはコマンド実行後に
    ' 図面が「比較モード」に移行した状態になる。
    
    ' 注意: VBAからDWGCOMPAREの「結果オブジェクト」に直接触るAPIは公開されていない。
    ' そのため、UI上の比較結果を維持したままSaveAsを行うのが最も現実的な自動化である。
End Sub

4. 実務における「落とし穴」と回避策

このコードをプロダクション環境に投入する際、以下の3点に留意せよ。

A. `Chr(34)`(ダブルクォーテーション)の徹底

パスにスペースが含まれている場合(例: `C:\My Project\Drawing.dwg`)、`SendCommand`に渡すパスを `”` で囲まなければ、コマンドはパスの途中で途切れてエラーになる。`Chr(34)` を使うのは、プロの作法だ。

B. ファイルロックのハンドリング

比較対象の図面が他のユーザーによって開かれている場合、`Documents.Open` は失敗する。実務ツールでは、`On Error` で捕捉し、「ファイルが使用中です。担当者に確認してください」といった人間が次に何をすべきかを示すメッセージを返すように設計しろ。

C. 比較結果の「保存」に関する思想

`DWGCOMPARE` は、現在の図面の表示状態(View)を変化させるものだ。
もし君のクライアントが「変更されたオブジェクトだけを抜き出した新しい図面が欲しい」と言ってきた場合、`DWGCOMPARE` だけでは不十分だ。その場合は、比較コマンド実行後に、「比較結果のレイヤー(DWGCOMPARE_…)」に属するオブジェクトのみを抽出し、新しい図面にコピーするという追加ロジックが必要になる。

5. 結論:エンジニアとしての矜持

自動化とは、単に作業を速くすることではない。「人間がミスをする余地を、設計によって排除すること」だ。

エンティティを一つずつ比較するコードを書くのは、エンジニアの仕事ではない。それは単なる「作業のコード化」だ。AutoCADが持つ最強のエンジンを理解し、それをいかに安全に、いかに確実に制御するか。それこそが、アーキテクトに求められる資質である。

この設計思想を胸に、次なる自動化プロジェクトに挑んでほしい。

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