AutoCAD VBAを掌握する極限の知見
第3章:図面ライフサイクルの支配者となれ 〜保存確認ダイアログの完全制御とメモリ最適化〜
AutoCAD VBAの自動化スクリプトを組む際、多くの開発者が直面する最初の、そして極めて厄介な壁が「図面を閉じる際の保存確認ダイアログ」だ。
一括処理(バッチ処理)やシステム間連携の最中、突然画面にポップアップする「変更を保存しますか?」というモーダルダイアログ。これによって夜間の自動処理が完全にフリーズし、翌朝に出社して絶望した経験を持つ者は少なくないはずだ。
本稿では、AutoCADのドキュメントライフサイクル、COMの裏側でうごめくオブジェクトの生死、そしてダイアログの完全制御に至るまで、シニアエンジニアが知るべき極限の知見を授ける。
—
1. AutoCADにおける「閉じる」のメカニズムと危険な罠
VBAから図面を閉じる際、通常は `Document.Close` メソッドを使用する。
しかし、このメソッドの仕様を正確に理解している者は意外と少ない。
‘ 【危険なアンチパターン】
‘ 変更が加えられた状態でこれを実行すると、条件によってダイアログが浮上する
ActiveDocument.Close
`Close` メソッドには引数を持たせることができる。
`Document.Close(SaveChanges As Boolean, [FileName As String])`
一見、`SaveChanges` に `False` を渡せば保存せずに閉じられそうに見えるが、AutoCADのバージョンや、データベースが「Dirty(変更あり)」状態であるかどうかの内部フラグ、さらには外部参照(Xref)のロード状態によって、この引数が無視されるケースが実務上存在する。
特に、システム間連携やバックグラウンドでの図面一括変換において、ダイアログによる処理の停止はシステム全体の崩壊を意味する。我々は、AutoCADの意思に関係なく、コード側で強制的に状態をねじ伏せる必要がある。
—
2. 実装:変更破棄(Discard)によるクローズの極意
確実に保存確認ダイアログを回避し、変更を破棄して図面を閉じるための実用コードを以下に示す。単に閉じるだけでなく、メモリリークを防ぐためのオブジェクト解放の作法も網羅したプロダクション品質のコードだ。
Option Explicit
Public Sub ForceCloseWithoutSaving()
Dim acadDoc As AcadDocument
Dim docName As String
‘ エラーハンドリングの要塞化
On Error GoTo ErrorHandler
‘ アクティブドキュメントの取得
Set acadDoc = ThisDrawing.Application.ActiveDocument
docName = acadDoc.Name
‘ 【極限知見】
‘ データベースのModifiedフラグを強制的にリセットすることはできないが、
‘ Closeメソッドの第1引数に False を明示し、かつSavedプロパティをTrueに偽装するアプローチもある。
‘ しかし、最も安全なのは Closeメソッドのオーバーロード動作を利用することである。
‘ 例外処理を考慮し、アラート表示を一時的に抑制する
acadDoc.Application.DisplayAlerts = False
‘ 第1引数: False = 変更を保存しない
‘ 第2引数: 省略(現在の名前で閉じる)
acadDoc.Close False
‘ メモリ最適化:オブジェクト参照の即時解放
Set acadDoc = Nothing
‘ アラート表示を復元
ThisDrawing.Application.DisplayAlerts = True
MsgBox “図面 ‘” & docName & “‘ は保存されずに閉じられました。”, vbInformation, “自動化エンジン”
Exit Sub
ErrorHandler:
‘ 障害発生時も必ずアラート表示とメモリ解放を担保する
ThisDrawing.Application.DisplayAlerts = True
Set acadDoc = Nothing
MsgBox “致命的なエラーが発生しました: ” & Err.Description, vbCritical, “Execution Error”
End Sub
—
3. レガシー環境とWindows APIによる「ダイアログの完全粉砕」
前述のコードで大半のケースはクリアできるサードパーティ製アドオンや、特定の外部コモンコントロールが絡むレガシーなAutoCAD環境(AutoCAD 2010〜2016世代など)では、それでもなおモーダルダイアログが割り込んでくることがある。
ここで登場するのが、Windows APIによるウィンドウ制御だ。
AutoCADのプロセスが生み出す子ウィンドウ(ダイアログ)を検出し、プログラム側から強制的に「いいえ(No)」ボタンを押下する、あるいはウィンドウハンドルを直接破棄するアプローチが、極限環境における最終防衛ラインとなる。
以下は、APIを用いた高度な制御の概念コードである。
‘ 32bit/64bit環境両対応のAPI宣言
If Win64 Then
Private Declare PtrSafe Function FindWindow Lib “user32” Alias “FindWindowA” (ByVal lpClassName As String, ByVal lpWindowName As String) As LongPtr
Private Declare PtrSafe Function SendMessage Lib “user32” Alias “SendMessageA” (ByVal hwnd As LongPtr, ByVal wMsg As Long, ByVal wParam As LongPtr, ByVal lParam As LongPtr) As LongPtr
Else
Private Declare Function FindWindow Lib “user32” Alias “FindWindowA” (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Private Declare Function SendMessage Lib “user32” Alias “SendMessageA” (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
End If
Private Const WM_CLOSE As Long = &H10
Public Sub KillAutoCADSaveDialog()
Dim hwndDlg As LongPtr
‘ AutoCADの保存確認ダイアログのウィンドウクラス名やタイトルを捕捉
‘ (※言語環境によりタイトル名が異なるため注意:英語圏なら “AutoCAD”, 日本語圏なら “AutoCAD メッセージ” 等)
hwndDlg = FindWindow(“#32770”, “AutoCAD”)
If hwndDlg <> 0 Then
‘ ダイアログが存在する場合、強制終了メッセージ(WM_CLOSE)を送信
SendMessage hwndDlg, WM_CLOSE, 0, 0
End If
End Sub
このAPIアプローチをVBAのタイマーイベントやループ処理と組み合わせることで、どんなに頑固なダイアログであろうとも、ユーザーの目に触れる前に消算することが可能となる。
—
4. チーフアーキテクトからの提言:メモリ最適化の真実
AutoCAD VBAにおいて、`Document` や `SelectionSet` などのCOMオブジェクトを適切に解放しないことは、メモリリーク(特に記憶域の断片化)を引き起こし、長時間のバッチ処理の途中でAutoCADが突然沈黙(クラッシュ)する主原因となる。
1. ローカル変数の `Set obj = Nothing` の徹底
スコープを抜ければ自動解放されるとは言え、COMの参照カウントが即座にデクリメントされる保証はない。明示的に `Nothing` を代入する習慣をつけろ。
2. `DisplayAlerts = False` の両刃の剣
ダイアログを消し去る強力な手段だが、致命的なエラー(ディスク容量不足やファイル破損など)をも隠蔽してしまう。必ず `Error` ハンドラ内で確実に `True` に戻すフェイルセーフ構造を実装すること。
図面ファイルのオープン・セーブ・クローズという基本動作の裏側には、OSのメモリ管理とAutoCAD独自のドキュメントマネジメントのせめぎ合いが存在する。この領域を完全に掌握した者だけが、真に安定した堅牢なCAD自動化システムを構築できる。
妥協のないコードで、次の自動化の地平へ進んでほしい。
