AutoCAD VBAを掌握する極限の知見:`AcadApplication.Quit`の完全制御とシャットダウンシーケンスの極意
外部プロセスや常駐バッチシステムからAutoCADを統御するアーキテクトにとって、最大の鬼門は「アプリケーションの終了フェーズ」にある。
特に、大量の図面をバッチ処理で次々と開き、解析・変換・修復を行った挙句、最後にプロセスを解放しようとした瞬間、あの悪名高いダイアログ——「変更を保存しますか? [はい(Y)/いいえ(N)/キャンセル(C)]」——によって、完全無人稼働を前提としたシステムが完全に沈黙した経験を持つ者は少なくないはずだ。
VBAの標準的な `ThisDrawing.Application.Quit` や `AcadApplication.Quit` は、実に無慈悲だ。編集フラグ(`Saved` プロパティ)が偽のドキュメントが1つでも存在すれば、AutoCADは内部イベントループを中断し、UIスレッド上にモーダルダイアログをポップアップさせる。親プロセスからの応答がない限り、システムは永遠に宙に浮く。
本稿では、このAutoCADのシャットダウンシーケンスを完全に掌握し、ユーザーの介入を一切排した上で、未保存図面の一括破棄(Discard)あるいは強制保存(Force Save)をプログラム側から完遂させるための「極限の知見」を授ける。
—
1. AutoCADオブジェクトモデルの闇:なぜ `Quit` はブロックされるのか
AutoCADのCOMインターフェースは、内部的にC++のCore(AcDb/AcAp)とCOMラッパー(`acad.exe`)の多重構造で成り立っている。
`AcadApplication` オブジェクトを通じて `Quit` メソッドを呼び出した際、AutoCADは以下のシーケンスを内部で実行する。
1. `AcadDocuments` コレクション内の全ドキュメントを走査。
2. 各 `AcadDocument.Saved` プロパティの状態を評価。
3. `False`(未保存の変更あり)のドキュメントを発見した場合:
- 外部自動化コンテキスト(Automation)であるかを判定。
- 判定の漏れやUIアタッチ状態によっては、強制的にユーザー対話モード(Modal Dialog)へフォールバック。
ここでシニアエンジニアが理解すべき核心は、「ドキュメントの保存状態の変更は、アプリケーションレベルの終了命令よりも、各ドキュメントレベルのライフサイクル管理に依存している」という点だ。つまり、`Quit` を叩く前に、すべてのドキュメントを「保存済み」の状態に偽装するか、あるいは明示的にクローズ処理を施す以外に道はない。
—
2. アプローチの比較:破棄か、強制保存か
無人バッチ処理におけるシャットダウン方針は、大別して2つの戦略に分かれる。
- 戦略A:変更の完全破棄(Discard & Quit)
- 用途: 参照用ビューア、一括変換バッチ、一時的なデータ抽出処理。
- 手法: 全ドキュメントの `Saved` プロパティを `True` に強制書き換えするか、`Close False` でドキュメント単位で破棄して `Quit` する。
- 戦略B:安全な自動保存(Force Save & Quit)
- 用途: 自動修復バッチ、属性値の一括置換システム。
- 手法: パスが通っているものは上書き保存、新規図面は規定のテンプレまたは自動生成パスへ強制保存した上で `Quit` する。
実務上、最もトラブルが多いのは「予期せぬ変更フラグの残留」によるハングアップである。これをコードレベルでねじ伏せる実装を見ていこう。
—
3. 実装コード:シャットダウンシーケンスの完全制御
以下に提示するのは、外部VBA(またはExcel VBA等からのLate/Early BoundによるAutoCAD制御)から呼び出されることを想定した、完全堅牢なシャットダウン制御モジュールである。
Option Explicit
‘ ==============================================================================
‘ 致命的なエラーや未保存図面によるハングアップを完全に排除する終了制御ルーチン
‘ ==============================================================================
Public Sub ForceShutdownAutoCAD(ByVal acadApp As Object, ByVal saveChanges As Boolean)
On Error GoTo ErrorHandler
Dim docs As Object
Dim doc As Object
Dim i As Long
If acadApp Is Nothing Then Exit Sub
‘ アプリケーションの可視性を一時的に隠す(UI描画とダイアログ割込みの防止)
On Error Resume Next
acadApp.Visible = False
On Error GoTo ErrorHandler
Set docs = acadApp.Documents
If Not docs Is Nothing Then
‘ コレクションの末尾からループを回す(安全な要素削除の基本)
For i = docs.Count0 To 1 Step -1
Set doc = docs.Item(i)
If Not doc Is Nothing Then
‘ デバッグ用トレース
Debug.Print “Processing Document: ” & doc.Name
If saveChanges Then
‘ — 戦略B: 強制保存パス —
If doc.Path = “” Then
‘ 未保存の新規図面の場合、テンポラリディレクトリ等に強制保存
Dim tempPath As String
tempPath = Environ(“TEMP”) & “\AutoCAD_AutoSave_” & Format(Now, “yyyymmdd_hhnnss”) & “.dwg”
doc.SaveAs tempPath
Debug.Print “New document saved to: ” & tempPath
Else
‘ 既存図面は上書き保存
doc.Save
End If
Else
‘ — 戦略A: 変更の完全破棄パス —
‘ 変更フラグを強制的にTrueに偽装することで、保存確認ダイアログをスキップさせる
doc.Saved = True
End If
‘ ドキュメントを完全に閉じる(変更を保存しない場合は False)
‘ ※Saved = Trueにしていれば、Close Falseでも保存ダイアログは出ない
doc.Close False
End If
Next i
End If
‘ 全ドキュメントの解放を確認した後、アプリを終了
acadApp.Quit
‘ オブジェクト参照の完全破棄(メモリリーク防止)
Set doc = Nothing
Set docs = Nothing
Exit Sub
ErrorHandler:
‘ 異常終了時のフォールバック:強制プロセス終了(Killプロセス等との併用)
Debug.Print “Error in ForceShutdownAutoCAD: ” & Err.Description
Resume Next
End Sub
コードのキモとアーキテクトの知見
1. 逆順ループ(`Count` から `1` へ)の鉄則
AutoCADのドキュメントコレクションに限らず、COMのコレクション要素をインデックスで操作しながら破棄する場合、順方向で回すとインデックスのズレ(Shift)が発生し、メモリ違反や取りこぼしが起きる。必ず末尾からのデクリメントで処理すること。
2. `doc.Saved = True` によるフラグのハック
AutoCADのCOM仕様において、`Saved` プロパティは読み書き可能である。これを強制的に `True` に書き換えることで、内部の「ダーティフラグ」をクリアし、閉じるときの確認ダイアログのトリガーを物理的に破壊する。
3. `Visible = False` によるUIスレッドの凍結
バックグラウンド処理の終盤でウィンドウを非表示にすることで、万が一ダイアログやメッセージボックスが発生したとしても、ユーザーの眼に触れず、かつフォーカス泥棒によるハングアップを防ぐことができる。
—
4. プロセスレベルの「外側からの撲殺」:最終防衛ライン
VBAコード側からどれほど丁寧にクリーンアップを試みても、アドオン(ARX/(.NET))のアンロード不良や、AutoCAD自体のフリーズによって `AcadApplication.Quit` が応答しないケースは実務上ゼロにはならない。
真に堅牢なエンタープライズ・システムを構築する場合、VBAの制御をすり抜けたゾンビプロセスを、Windows OSのAPI(またはシェルコマンド)を用いて強制刈り取りする「最終防衛ライン」を必ず用意すべきである。
‘ ==============================================================================
‘ ゾンビ化したAutoCADプロセス(acad.exe)を強制終了する
‘ ==============================================================================
Public Sub KillAllAutoCADProcesses()
Dim wmi As Object
Dim processes As Object
Dim process As Object
Set wmi = GetObject(“winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2”)
Set processes = wmi.ExecQuery(“Select from Win32_Process Where Name = ‘acad.exe'”)
For Each process in processes
On Error Resume Next
process.Terminate
On Error GoTo 0
Next process
Set process = Nothing
Set processes = Nothing
Set wmi = Nothing
End Sub
注意:この `KillAllAutoCADProcesses` は、他のユーザーが同じ端末で正当に作業しているCAD図面をも巻き込んで強制終了させるため、単一の専用仮想マシン(VM)やバッチ専用サーバー環境でのみ使用すべき最終手段である。
—
5. チーフアーキテクトからの総括
AutoCAD VBAを用いた自動化の成否は、突き詰めれば「リソースのライフサイクルと例外状態のコントロール」に他ならない。
`AcadApplication.Quit` という一見シンプルに見えるメソッドの裏側には、CAD特有のドキュメント管理システムとWindowsのCOMアーキテクチャの複雑なせめぎ合いが存在する。本稿で解説した「フラグの制御」「逆順ループによる破棄」「UIの非表示化」、そして「WMIによるプロセス隔離」のコンビネーションを実装に組み込むことで、あなたは二度と「未保存図面のダイアログによるバッチ停止」という悪夢に悩まされることはなくなるはずだ。
技術とは、泥臭い例外や仕様の隙間を、揺るぎないロジックでねじ伏せるためにある。あなたのアーキテクチャに、この知見を直ちに実装せよ。
