AutoCAD VBAの深淵:オブジェクト削除の最適化と「死なない」コードの設計思想
AutoCAD VBAにおいて、オブジェクトの削除は単純な `Delete` メソッドの呼び出しではない。それは、メモリ空間に存在するCOMオブジェクトの断片を、いかに安全かつ高速に揮発させるかという「ライフサイクル管理」の問題である。
多くのエンジニアは `For Each` 文でオブジェクトをなめ回し、条件に合致すれば即座に `Delete` を投げる。しかし、これはプロフェッショナルの仕事ではない。削除中にイテレータが破綻するリスク、未定義の参照によるメモリリーク、そして何より「図面データベースの整合性」を無視した実装は、必ず将来の技術的負債となる。
本稿では、レガシー環境を生き抜くための、堅牢かつ高速なオブジェクト削除の極意を伝授する。
—
1. 削除の定石:順方向イテレーションの罠を回避する
AutoCADの `ModelSpace` や `Block` オブジェクトを `For Each` で回しながら `Delete` を実行すると、インデックスがずれる可能性がある。特に、複雑なネストやフィルタリングを行う場合、これは致命的だ。
解決策: 削除対象を一度「配列」または「コレクション」に退避(スタック)し、イテレーション終了後に一括削除する。これがメモリとプロセスを安定させる鉄則である。
実装の極意:フィルタリングとスタック処理
Public Sub SafeDeleteByCriteria()
Dim acadObj As AcadEntity
Dim deleteStack As Collection
Set deleteStack = New Collection
‘ メモリ管理: 不要なオブジェクト参照の発生を抑制するため、
‘ ループ内では最小限のプロパティ参照に留める
For Each acadObj In ThisDrawing.ModelSpace
If IsTargetForDeletion(acadObj) Then
deleteStack.Add acadObj
End If
Next acadObj
‘ スタックから逆順で削除(あるいは順次削除)
‘ ※削除後にオブジェクト変数をNothingにするのを忘れないこと
Dim i As Long
For i = deleteStack.Count To 1 Step -1
deleteStack.Item(i).Delete
Next i
‘ メモリの強制解放を促す
Set deleteStack = Nothing
End Sub
Private Function IsTargetForDeletion(obj As AcadEntity) As Boolean
‘ ここに高速な評価ロジックを記述する
‘ レイヤー、色、線種などの判定を早期リターンで実装し、
‘ キャッシュ可能な属性は変数に格納しておく
If obj.Layer = “TEMP_LAYER” Then
IsTargetForDeletion = True
End If
End Function
—
2. パフォーマンスの深淵:`SelectionSets` を活用したバルク操作
個別のオブジェクトを `For Each` で判定するのは、数万エンティティを有する図面では自殺行為だ。AutoCADのデータベースは「選択セット(SelectionSets)」によるフィルタリング機能が極めて強力である。
COM APIの `Select` メソッドにDXFコード(コード 8=画層, 62=色など)を渡すことで、AutoCAD内部のC++エンジン側でフィルタリングを実行させる。これはVB側のループを回すよりも桁違いに速い。
‘ 選択セットを用いた爆速削除のテンプレート
Public Sub BulkDeleteByLayer(layerName As String)
Dim sset As AcadSelectionSet
Dim filterType(0) As Integer
Dim filterData(0) As Variant
‘ 既存の同名選択セットをクリア
On Error Resume Next
ThisDrawing.SelectionSets.Item(“DelSet”).Delete
On Error GoTo 0
Set sset = ThisDrawing.SelectionSets.Add(“DelSet”)
filterType(0) = 8 ‘ DXFコード: 画層
filterData(0) = layerName
‘ データベース内部エンジンで高速抽出
sset.Select acSelectionSetAll, , , filterType, filterData
‘ 一括で削除
sset.Erase
sset.Delete
End Sub
—
3. Windows APIによる「強制停止」と「進捗表示」
大規模な図面での削除処理中、AutoCADは「応答なし」に見えることがある。ユーザーに不安を与えないためには、Windows APIを利用して進捗をプログレスバーで表示したり、処理を一時停止させる機能が求められる。
また、巨大な図面を扱う際は `DoEvents` を適切に配置し、メッセージキューを処理させる必要があるが、多用は厳禁だ。コンテキストスイッチのオーバーヘッドが致命的な速度低下を招く。
—
4. チーフアーキテクトからの提言:クリーンな環境への責任
最後に、最も重要なのは「エラーハンドリング」と「オブジェクトの解放」である。
- 明示的な Nothing 代入: オブジェクト型変数には、必ず最後に `Set var = Nothing` を明示せよ。VBAのガベージコレクションを信じてはいけない。特にAutoCADの `AcadDocument` ライフサイクルは複雑であり、親オブジェクトが生きている限りメモリは解放されない。
- Undo記録の制御: 削除処理を `StartUndoMark` と `EndUndoMark` で囲むことを忘れてはならない。これを怠ると、ユーザーは誤削除から復帰できず、あなたのコードは「破壊者」として記憶される。
究極のアーキテクチャ・チェックリスト
1. SelectionSets で可能な限り絞り込み、ループの回数を最小化しているか。
2. Undo の境界を定義し、トランザクションの安全性を確保しているか。
3. On Error Resume Next を乱用せず、スタックの整合性を保っているか。
AutoCAD VBAはレガシーだが、その設計思想を理解すれば、最新の.NET APIであっても通用する「本質」が見えてくるはずだ。コードは書き捨てるものではなく、図面という資産を守るための鎧である。妥協なき実装を期待する。
