【AutoCAD VBAを掌握する極限の知見】ThisDrawingとAcadApplication.ActiveDocumentの決定的な違いと使い分けの罠
長年、大規模なプラント設計からインフラの自動化に至るまで、数千個の図面を一括処理するミッションクリティカルなAutoCAD VBAシステムを構築・保守してきた。その中で、新人からベテランまでが最も深くハマり込み、夜間バッチを全滅させる悪夢の元凶がある。
それが、`ThisDrawing` と `AcadApplication.ActiveDocument` の混同だ。
「どちらを使っても現在の図面が取れるから同じだろう」という甘い認識は、マルチドキュメント環境(SDI/MDI)や、背後で非同期に動くイベントハンドラ、さらにはCOMオブジェクトのライフサイクル管理において、致命的なバグを引き起こす。
今回は、AutoCADのオブジェクトモデルの深層に潜む「コンテキストの罠」を解き明かし、複数図面を正確無比に制御するためのコード設計の極意を伝授する。
—
1. オブジェクトモデルの根幹:なぜこの2つは別物なのか?
AutoCADのVBA環境において、オブジェクト階層の頂点には常に `AcadApplication` が存在する。しかし、私たちが日々コードを記述し、図形を描画するキャンバスは `AcadDocument` である。
この2者の最も決定的な違いは、「誰を基準(コンテキスト)に参照しているか」という点にある。
- `ThisDrawing`
- 正体: 標準モジュールやThisDrawingモジュールに暗黙的にバインドされた、「コードが記述されている、あるいは現在コンテキストが固定されている特定のドキュメント」を指す固有のポインタ(厳密には `ActiveDocument` とは限らない遅延バインドの特殊参照)。
- `AcadApplication.ActiveDocument`
- 正体: AutoCADのアプリケーション全体から見た、「現在、ユーザーインターフェース上でアクティブ(最前面でフォーカスを持っている)なドキュメント」への動的な参照。
この「静的(固定)」と「動的(変動)」の性質の違いを理解していないと、マルチドキュメント(複数図面同時オープン)の現場でシステムは容易に崩壊する。
—
2. 現場を崩壊させる「ActiveDocument」の罠
想像してほしい。
あなたは100枚の図面を一括処理する夜間バッチマクロを組んだ。処理を高速化するため、図面を開いた状態で裏でループを回し、図形情報を抽出している。
しかし、そのバッチ処理の最中に、オペレーターが別の図面をクリックしてアクティブにしてしまった。あるいは、Windowsのフォーカス競合によって別図面が前面に出た。
その瞬間、`AcadApplication.ActiveDocument` が指す先は、バッチ処理対象の図面から、オペレーターが触った無関係の図面へと切り替わる。
結果、前者の図面に対する処理が後者の図面に対して実行され、データが破損する――これが「アクティブドキュメント依存の呪い」である。
シニアが避けるべき「アンチパターン」コード
‘ 【悪夢のアンチパターン】
‘ 複数図面を開いた状態でこれを実行すると、ユーザーの操作でターゲットがすり替わる
Sub BadBatchProcess()
Dim i As Long
For i = 1 To 10 ドキュメント数
‘ ユーザーが別の図面をクリックすると、ActiveDocumentの指す実体が変わり、
‘ 意図しない図面をゴリゴリ書き換えてしまう!
AcadApplication.ActiveDocument.ModelSpace.AddLine Point1, Point2
Next i
End Sub
—
3. 領域別の適切な使い分け:標準モジュール vs クラスモジュール
アーキテクチャを設計する際、ドキュメント参照のスコープは厳格に分離しなければならない。
① 標準モジュール(.bas)での基本方針
標準モジュール内では、原則として `ThisDrawing` を用いるべきではない。なぜなら、標準モジュールはどの図面からも呼び出せる汎用的なロジックを置く場所であり、「どの図面を対象にしているか」が曖昧になりやすいためだ。
複数図面を横断するユーティリティを書く場合は、必ず関数やサブルーチンの引数として `AcadDocument` オブジェクトを明示的に渡す設計(Dependency InjectionのVBA版)を採用するべきである。
② クラスモジュール(.cls)での決定的な違い
クラスモジュール(例えば、イベント監視やカスタムエンティティのラッパー)において、`ThisDrawing` はコンパイルエラーになるか、あるいは意図しないインスタンスを指して沈黙する。
クラス内では、明確に `AcadApplication.ActiveDocument` または、イベントの引数として渡される `Document` オブジェクトをローカル変数に保持して制御するべきだ。
—
4. 【実践】複数図面を安全に操る堅牢なコード設計
では、意図しない図面の操作を完全に防ぎ、メモリリークをも回避する堅牢なコードとはどのようなものか。
以下の実用コードを見てほしい。これは、開いているすべての図面に対して安全に処理を行い、かつオブジェクトの参照を適切に解放するプロフェッショナル・テンプレートである。
Option Explicit
‘ ==============================================================================
‘ 堅牢な複数図面一括処理の模範実装
‘ ==============================================================================
Sub SafeProcessAllDocuments()
Dim acadApp As AcadApplication
Dim targetDoc As AcadDocument
Dim i As Long
‘ 1. アプリケーションインスタンスの確実な取得(早期バインド推奨)
On Error Resume Next
Set acadApp = GetObject(, “AutoCAD.Application”)
If err.Number <> 0 Then
MsgBox “AutoCADが起動していません。”, vbCritical
Exit Sub
End If
On Error GoTo 0
‘ 2. 開かれているドキュメントが存在するかチェック
If acadApp.Documents.Count = 0 {
MsgBox “処理対象の図面が開かれていません。”, vbExclamation
Exit Sub
}
‘ 3. ActiveDocumentへの依存を断ち切り、コレクションから明示的にインデックスで取得する
‘ ここがポイント:ActiveDocumentを使わず、Documents(i)を直接叩くことで、
‘ ユーザーが裏でどの図面を触っていようとも、ターゲットを完全固定できる。
For i = 0 To acadApp.Documents.Count – 1
Set targetDoc = acadApp.Documents(i)
‘ 読み取り専用や特殊な図面を除外する安全策
If Not targetDoc.ReadOnly Then
Call ProcessSingleDocument(targetDoc)
End If
Next i
‘ 4. クリーンアップ(COMオブジェクトの明示的解放)
Set targetDoc = Nothing
Set acadApp = Nothing
MsgBox “全図面のバッチ処理が正常に完了しました。”, vbInformation
End Sub
‘ ==============================================================================
‘ 個別図面に対する処理ロジック(依存性注入の適用)
‘ ==============================================================================
Private Sub ProcessSingleDocument(ByVal doc As AcadDocument)
Dim acadEnt As AcadEntity
Dim msg As String
‘ 引数で渡された特定のドキュメントコンテキスト内で安全に操作を実行
msg = “処理中: ” & doc.Name
// Debug.Print msg
‘ 例:モデル空間内の全エンティティを走査
For Each acadEnt In doc.ModelSpace
‘ ここに高度な幾何学処理やレイヤ変更などを記述
Next acadEnt
‘ ドキュメント固有のオブジェクト参照を破棄
Set acadEnt = Nothing
End Sub
—
5. チーフアーキテクトからの提言:メモリ最適化とCOMの作法
AutoCAD VBAにおいて、`Set obj = Nothing` によるオブジェクトの明示的な解放を怠ると、VBAの背後で動くCLRやCOMの参照カウンタが減少しず、AutoCADプロセス(acad.exe)がメモリリークを起こす。特に複数図面をループで回すようなマクロでは、イテレーションごとに確実にローカル変数を `Nothing` にクリアすることが、長時間の安定稼働を担保する唯一の道だ。
- `ThisDrawing` は「いま手元にある固有のコンテキスト」への甘えを生むショートカットに過ぎない。
- 大規模、あるいはミッションクリティカルなシステム開発において、「暗黙的な参照」ほど恐ろしいものはない。
常に `AcadApplication` を起点とし、操作対象の `AcadDocument` を変数にバインドして処理を完結させる――この鉄則を守るだけで、あなたの書くAutoCAD VBAの信頼性は、プロフェッショナルの領域へと昇華される。
