AutoCAD VBAの深淵:メモリリークを根絶し、図面オープン・セーブの「完全制御」を手に入れる
AutoCAD VBAは、単なる「マクロの記録」の延長ではない。COM(Component Object Model)の薄いラッパーを纏った、極めて繊細なメモリ管理が求められる戦場だ。
多くのエンジニアが、図面の大量処理中に「AutoCADが徐々に重くなる」「特定回数で必ずクラッシュする」という現象に頭を抱える。原因は明確だ。COMオブジェクトの参照カウントに対する理解不足と、VBAのガベージコレクションへの甘えである。
本稿では、レガシーなAutoCAD環境において、システムを極限まで安定させるためのメモリ管理術を伝授する。
—
1. なぜ「Close」だけでは不十分なのか
VBAの`Document.Close`メソッドを呼ぶだけでは、実はメモリ上のオブジェクトは完全には解放されていない。COM参照はバックグラウンドで生き続け、GC(ガベージコレクション)のタイミングを待つが、AutoCADのメモリ管理は決して効率的とは言えない。
特に`AcadDocument`オブジェクトや`AcadDatabase`オブジェクトをループ内で扱う際、明示的な解放を行わないことは、時限爆弾を抱えるに等しい。
極意:オブジェクトの「強制無効化」
VBAにおいてオブジェクトを解放する唯一の手段は、`Nothing`を代入することだが、それだけでは足りない場合がある。特に上位APIを併用する場合、参照カウントのデクリメントを確実に行う必要がある。
‘ メモリリークを防止するための標準的かつ堅牢なファイルオープン制御
Public Sub SafeOpenAndClose(ByVal filePath As String)
Dim doc As AcadDocument
‘ エラーハンドラで必ず解放処理を通すのが鉄則
On Error GoTo Cleanup
Set doc = Application.Documents.Open(filePath)
‘ — ここに図面処理ロジックを記述 —
doc.Close SaveChanges:=False
Cleanup:
‘ 参照を明示的に解除する
‘ これを怠ると、VBAの参照カウンタは0にならず、メモリ上に残骸が溜まる
If Not doc Is Nothing Then
Set doc = Nothing
End If
‘ 強制的なメモリ回収のヒント:VBA単体では限界があるが、
‘ アプリケーション終了時や特定処理後にDoEventsを挟むことで
‘ メッセージキューを処理し、COMの解放を促す効果がある
DoEvents
End Sub
—
2. Windows APIによる「生存確認」と「強制終了」
AutoCADのAPIだけでは、図面が「読み取り専用」でロックされたり、裏でゾンビプロセスとして生き残るケースがある。これを制御するには、Windows APIを直接叩き、プロセスレベルで介入する勇気が必要だ。
特に、大量のDWGをバッチ処理する際、AutoCADが「応答なし」になった場合にプロセスを強制終了させ、次へ進むための監視ルーチンは必須である。
プロセス監視の極知見
`TerminateProcess`を闇雲に使うのは危険だが、最終手段としては不可欠だ。
If VBA7 Then
Private Declare PtrSafe Function OpenProcess Lib “kernel32” (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As LongPtr
Private Declare PtrSafe Function TerminateProcess Lib “kernel32” (ByVal hProcess As LongPtr, ByVal uExitCode As Long) As Long
End If
‘ ゾンビプロセスを狩るための緊急用ルーチン
Public Sub KillAutoCADProcess(ByVal procID As Long)
Dim hProcess As LongPtr
hProcess = OpenProcess(&H1, 0, procID)
If hProcess <> 0 Then
TerminateProcess hProcess, 0
End If
End Sub
—
3. バージョン互換制御の真髄:SaveAsとSaveAsV2
古いバージョンのDWG形式へ変換する際、`SaveAs`メソッドの引数指定を誤ると、後続のプログラムがそのファイルにアクセスできなくなる現象が起きる。
システム間連携において最も重要なのは、「保存時のバージョンと、保存後のオブジェクト解放のタイミング」だ。
- 極限のヒント: `SaveAs`を実行した直後、AutoCADは内部的にデータベースの再ロードを行う場合がある。この瞬間に`Set doc = Nothing`を行うと、内部的な参照の不整合で例外が発生する。
- 対策: `SaveAs`の後には必ず`DoEvents`を入れ、COMのイベントループが安定するのを待て。
‘ 互換性を考慮した安全な保存ルーチン
Public Sub SaveAsVersion(ByVal doc As AcadDocument, ByVal savePath As String, ByVal version As AcSaveAsType)
‘ バージョンを指定して保存
doc.SaveAs savePath, version
‘ 保存直後はCOMがビジー状態になりやすいため、数ミリ秒の待機とイベント処理を強制する
DoEvents
‘ ここで初めてDocumentを閉じる
doc.Close False
End Sub
—
結論:アーキテクトとしての心構え
AutoCAD VBAでメモリリークを防ぐための唯一の絶対法則は、「オブジェクト参照のスコープを最小化し、終了処理をカプセル化すること」に尽きる。
1. プロシージャを細分化する: 巨大な関数で図面を開き続けるな。処理単位で関数を分け、スコープを抜ける瞬間にオブジェクトが確実に破棄される設計にせよ。
2. エラーハンドリングを怠るな: 異常終了時にメモリが解放されないのがリークの最大の温床だ。全プロシージャに`Cleanup`ラベルを設置せよ。
3. レガシーを恐れるな: 現代の64bit環境であっても、VBAは32bitのCOMアーキテクチャの上にある。OS側のメモリ管理に頼らず、自分自身でリソースを管理する意識こそが、シニアエンジニアの証明である。
このコードを書き終えたとき、君のシステムはもはや単なるマクロの集合体ではない。堅牢なオートメーション・エンジンへと昇華されているはずだ。健闘を祈る。
