【テクニカル・上級編】【上級】AcadApplicationのCOM参照を完全に解放し、タスクマネージャにAutoCADを残さない終了処理 – AutoCAD VBA解析バイブル

スポンサーリンク

【上級】AcadApplicationのCOM参照を完全に解放し、タスクマネージャにAutoCADを残さない終了処理

VBAの裏側でAutoCADを外部起動し、図面のバッチ処理やデータ抽出を行った後、ふとタスクマネージャを開いて絶望したことはないだろうか。
――そこには、VBAの実行がとうに終わっているにもかかわらず、メモリを数ギガバイト食いつぶしたまま居座り続ける無数の `acad.exe`(通称:ゾンビプロセス)の姿が。

この問題は、単に `Set acadApp = Nothing` と書けば解決するという甘いものではない。COM(Component Object Model)の参照カウンタ、VBA固有のガベージコレクションの気まぐれ、そしてAutoCAD自体のプロセスライフサイクルが複雑に絡み合う、AutoCAD VBA開発者にとっての永遠の呪縛である。

本稿では、このゾンビプロセスを根絶やしにし、OSのメモリ空間を完全にクリーンに保つための極限の知見を、COMの深部から解き明かす。

1. なぜ「ゾンビプロセス」は生まれるのか?

AutoCADを外部(VBAの `CreateObject` や `GetObject`)から操作する場合、WindowsのCOM IPC(プロセス間通信)を介して `AcadApplication` オブジェクトへの参照を保持することになる。

COMの世界では、オブジェクトの生存期間は参照カウンタ(Reference Counter)によって管理されている。
1. アプリケーションがオブジェクトを取得する $\rightarrow$ 参照カウンタ `+1`
2. 変数がスコープを抜ける、または `Nothing` が代入される $\rightarrow$ 参照カウンタ `-1`
3. 参照カウンタが `0` になった瞬間、プロセスは自発的に終了する

しかし、VBAという言語のランタイム(`VBE7.DLL`)は、独自のメモリ管理とガベージコレクションを行っているため、「コード上は見えなくなった変数」のCOM参照が、内部のポインタテーブルに幽霊のように残留するケースが多々発生する。特に、複数のオブジェクト(`AcadDocument`, `AcadModelSpace` など)をドット繋ぎで連続取得(メソッドチェーン)した瞬間、参照の連鎖を解除するすべを失い、メモリリークとゾンビ化へのカウントダウンが始まる。

2. ゾンビ化を防ぐための鉄則:3つの大原則

実務において、タスクマネージャにAutoCADを残さないためには、以下の3つの原則を泥臭く、かつ厳密に守り抜く必要がある。

1. すべてのCOMオブジェクトを個別の変数で捕捉し、逆順で `Nothing` を代入する
2. `AcadApplication.Quit` を明示的に呼び出す
3. エラーハンドリング(`On Error GoTo`)を必ず実装し、異常終了時でも確実に解放処理を通す

特に重要なのは「解放の順序」だ。アプリケーションから階層を下って取得したオブジェクト(Document $\rightarrow$ SelectionSet $\rightarrow$ Entity など)を解放する前に、大元の `AcadApplication` を解放してはならない。必ず末端のオブジェクトから逆順に `Nothing` を代入し、最後にApplicationを解放するのが鉄則である。

3. 【実践】完全無欠の終了処理テンプレート(VBAコード)

以下に、実務の現場で即座に採用できる、ゾンビを一切発生させない堅牢なVBAプロシージャの模範解答を示す。

Option Explicit

Sub ExecuteAutoCADAutomationSafely()
Dim acadApp As Object
Dim acadDoc As Object
Dim mSpace As Object
Dim isAppStartedByMe As Boolean

isAppStartedByMe = False

On Error GoTo ErrorHandler

‘ — 1. AutoCADインスタンスの取得(起動またはアタッチ) —
On Error Resume Next
Set acadApp = GetObject(, “AutoCAD.Application”)
If acadApp Is Nothing Then
‘ 起動していなければ新規起動
Set acadApp = CreateObject(“AutoCAD.Application”)
If acadApp Is Nothing Then
MsgBox “AutoCADの起動に失敗しました。”, vbCritical
Exit Sub
End If
isAppStartedByMe = True
End If
On Error GoTo ErrorHandler ‘ エラー監視を復帰

‘ 可視化の制御(バックグラウンド処理ならFalse、デバッグならTrue)
acadApp.Visible = True

‘ — 2. ドキュメントとオブジェクトの操作 —
Set acadDoc = acadApp.Documents.Add(“acad.dwt”)
Set mSpace = acadDoc.ModelSpace

‘ (ここに実際の自動化処理を記述)
‘ 例: 簡易的なログ出力など
Debug.Print “現在処理中の図面: ” & acadDoc.Name

‘ 正常終了時の処理
GoTo CleanUp

ErrorHandler:
‘ 予期せぬエラー発生時のログ出力など
MsgBox “エラーが発生しました: ” & Err.Description, vbCritical

CleanUp:
‘ — 3. 厳格な逆順オブジェクト解放プロセス —
‘ 末端のオブジェクトから順に参照を完全に断ち切る

If Not mSpace Is Nothing Then
Set mSpace = Nothing
End If

If Not acadDoc Is Nothing Then
‘ 必要に応じて保存処理(今回は保存せずに閉じる例)
‘ acadDoc.Close False
Set acadDoc = Nothing
End If

If Not acadApp Is Nothing Then
‘ 自前で起動したインスタンスの場合のみ、Quitを明示的に叩く
‘ 既存のインスタンスにアタッチした場合はQuitしてはならない(ユーザーの作業を奪うため)
If isAppStartedByMe Then
On Error Resume Next
acadApp.Quit
On Error GoTo 0
End If

Set acadApp = Nothing
End If

‘ — 4. VBAガベージコレクションの強制駆動 —
‘ 参照カウントのデクリメントをVBAランタイムに強要する
DoEvents

Exit Sub
End Sub

4. チーフアーキテクトからの高度な補足:なぜ `DoEvents` が必要なのか?

上記のコードの最後に `DoEvents` を挟んでいる点に気づいただろうか。これは単なるおまじないではない。

VBAはシングルスレッドで動作するインタプリタ言語であり、COMオブジェクトの解放要求(Release)は、内部のメッセージキューやガベージコレクションのタイミングと非同期で処理されることがある。プロシージャの終端で一気に変数が破棄される際、OSのCOMランタイムへの通知が間に合わず、プロセスが取り残されるケースがごく稀に発生する。

`DoEvents` を挿入することで、Windowsメッセージループを一度フラッシュし、COMの解放シグナルを確実にオペレーティングシステムへ伝播させることができる。この一手間が、大規模なバッチ処理で何十個ものAutoCADインスタンスを立ち上げるようなシステムにおいて、メモリリークを完全に防ぐ防壁となる。

5. まとめ

AutoCAD VBAにおけるゾンビプロセス問題は、言語の仕様とCOMの構造的宿命が生み出す「必然」である。しかし、オブジェクトの生存期間を完全に把握し、「末端からの逆順解放」「所有権に応じたQuitの制御」「DoEventsによるメッセージフラッシュ」を徹底すれば、タスクマネージャを常にクリーンに保つことは十分に可能である。

プロフェッショナルなエンジニアたる者、動くだけのコードで満足してはならない。リソースを美しく解放し、後腐れなくシステムを終了させるコードこそが、真に信頼されるアーキテクチャの証なのである。

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