AutoCAD VBAの呪縛を解く:DoEventsとモードレスフォームによる「応答なし」の排除
AutoCAD VBAで数千の図面をバッチ処理する際、多くのエンジニアが「応答なし」という文字と格闘する。Windowsのメッセージキューが飽和し、AutoCADがフリーズしたように見えるあの現象だ。
単に `DoEvents` をループに放り込めば解決すると思っているなら、それは大きな間違いだ。不適切な `DoEvents` の乱用は、スタックオーバーフローやパフォーマンスの劇的な低下、さらには予期せぬ再帰的なイベント呼び出しを誘発する。
今日は、AutoCADという巨大なメモリ空間を制御し、いかにして「エレガントに進捗を可視化するか」という極限の知見を共有する。
—
1. 「応答なし」の正体とDoEventsの真実
AutoCADはシングルスレッドのアーキテクチャ上で動く。メインスレッドが重い計算やDBアクセスに占有されると、Windowsからの「描画更新要求」や「入力監視」が処理できなくなる。これが「応答なし」の正体だ。
`DoEvents` は、溜まっているメッセージキューを一度OSに返却する。しかし、これを過剰に行うと、「処理の実行中にユーザーが再度ボタンを押せてしまう」という致命的な競合状態を招く。
禁忌:ループ内の無秩序なDoEvents
‘ 絶対にやってはいけない例
For i = 1 To 10000
‘ 重い処理
DoEvents ‘ 毎ループ呼び出すと処理速度が1/10以下になる
Next i
—
2. 進捗可視化のアーキテクチャ:モードレスUserFormの構築
進捗バーを実装するには、`UserForm` の `ShowModal` プロパティを `False` に設定する。これにより、ユーザーはAutoCADのメインウィンドウを操作しながら、処理の進捗を監視できる。
UserFormの設定
1. `ShowModal` を `False` に設定。
2. フォーム内に `Label` (進捗バー用) と `Label` (進捗率表示用) を配置。
メモリを汚さないための制御ロジック
進捗更新は、処理の「1%ごと」あるいは「一定回数ごと」に限定せよ。タイマーを回すのではなく、カウンターによる間引き処理が最速だ。
‘ 処理間隔を制御する定数
Private Const UPDATE_INTERVAL As Long = 50
Public Sub UpdateProgress(ByVal current As Long, ByVal total As Long)
Static lastUpdate As Long
‘ 間引き処理:毎ループ更新はオーバーヘッドが大きすぎる
If current Mod UPDATE_INTERVAL <> 0 And current <> total Then Exit Sub
Dim pct As Double
pct = current / total
‘ フォームのUI更新
With Me
.lblBar.Width = 200 pct ‘ 200はバーの最大幅
.lblPercent.Caption = Format(pct, “0%”)
.Repaint ‘ 強制再描画
End With
‘ メッセージキューのフラッシュ
DoEvents
End Sub
—
3. オブジェクトライフサイクルとメモリ解放の極意
AutoCAD VBAにおいて、`AcadDocument` や `AcadSelectionSet` の解放漏れは、長時間バッチ処理において致命的な「メモリリーク」を引き起こす。特に `Set` で生成したオブジェクトは、明示的に `Nothing` を代入し、スコープを適切に管理せよ。
また、`DoEvents` 中にユーザーが強制終了(×ボタン押下)した場合に備え、グローバルなフラグ `AbortProcess` を用意しておくことが、シニアエンジニアの嗜みである。
‘ 処理の中断を安全に行うためのグローバル変数
Public bAbort As Boolean
Sub MainProcess()
Dim doc As AcadDocument
Dim ss As AcadSelectionSet
‘ UserFormをモードレスで表示
frmProgress.Show vbModeless
For Each doc In Documents
If bAbort Then Exit For ‘ ユーザーによる中断検知
‘ 処理実行
‘ …
‘ 明示的なオブジェクト解放
Set ss = Nothing
Next doc
Unload frmProgress
End Sub
—
4. さらに深淵へ:Windows APIによる「応答なし」完全撲滅
究極の安定を求めるなら、`DoEvents` だけでなく、`PeekMessage` API を使用して、特定のイベント(WM_PAINTやWM_QUIT)だけを処理する手法がある。これにより、ユーザーの不必要なクリック入力を無視しつつ、描画だけを更新する「堅牢なバッチ処理」が実現可能だ。
- Tips: `DoEvents` はすべてのメッセージを処理してしまうため、ボタン連打による再帰呼び出しのリスクがある。`PeekMessage` を使えば、描画メッセージ以外の入力をフィルタリングできる。
—
シニアエンジニアへの提言
AutoCAD VBAは、現代の言語から見ればレガシーかもしれない。しかし、その上で動く膨大な設計資産を操るには、オブジェクトモデルの挙動、メモリの生存期間、そしてOSとの対話作法を完全に掌握する必要がある。
「動けばいい」コードから「止まらない、止まらせない」アーキテクチャへ。
進捗バー一つとっても、そこにエンジニアの哲学が宿る。
現場のコードは、常に美しく、そして冷徹なまでに効率的であるべきだ。健闘を祈る。
