【AutoCAD VBA極限活用】図面内の全オブジェクトを属性フィルタリングせよ!AcadDocumentとAcadObjectを極めるメモリ管理と高速走査の極意
開発現場のリーダーである私から、ハッキリと言わせてもらう。
「図面内の特定の画層やオブジェクトをごっそり抽出し、集計・修正する」という作業において、ネットの片隅にあるような場当たり的なVBAコードをそのまま本番環境で動かしていないだろうか?
数万〜数百万のエンティティを持つ実務図面で、思考停止したループ処理を回せば、AutoCADはフリーズし、最悪の場合はクラッシュして大切な未保存データを吹き飛ばす。それがAutoCAD VBAの現実だ。
今回は、`AcadDocument` と `AcadObject` を完全掌握し、メモリリークを絶対に起こさず、かつ実務の膨大なデータ量を一瞬でさばく「堅牢なフィルタリング・エンジン」の設計思想と実装を伝授する。
—
1. なぜ「普通のVBAコード」は実務で破綻するのか?
多くのプログラマ(そして自称シニアエンジニア)が犯す最大の過ちは、`ModelSpace` や `PaperSpace` のオブジェクトを単純な `For Each` で全走査し、その都度 `If` 文で条件分岐させることだ。
ここに致命的な問題が2つある。
1. COMオブジェクトの参照解放漏れ(メモリリーク)
VBAからAutoCADのCOMラッパー(`AcadEntity` 等)にアクセスするたび、背後でポインタが生成される。これを適切に解放しないままループを回すと、ExcelやCADのプロセスが肥大化し、メモリ不足エラーを引き起こす。
2. 非効率なクエリ処理
VBA側で一つずつオブジェクトの `.Layer` や `.EntityName` を評価するのは、ボトルネックでしかない。AutoCADには、C++のネイティブ層で爆速かつ安全にデータを絞り込める「SelectionSet(選択セット)」という強力な武器が備わっている。
実務で求められるのは、この `SelectionSet` をVBAから完璧にコントロールし、かつ型安全にデータを処理するアーキテクチャだ。
—
2. 堅牢な設計:SelectionSetによる高速フィルタリング
オブジェクトの属性フィルタリングを行う際、最も効率的なのは DXFグループコード を用いた `Select` メソッドの活用だ。VBA側でループを回して判定するのではなく、AutoCADのエンジン側に条件を投げて、最初から合致したオブジェクトだけを抽出する。
これこそが、プロのエンジニアが選ぶべき「非同期・高効率な設計」である。
—
3. 【プロダクションコード】実務仕様の属性フィルタリング・エンジン
以下のコードは、指定した「画層名」と「オブジェクトタイプ」を条件に、図面内から該当するオブジェクトを完璧に抽出し、イミディエイトウィンドウへのログ出力と集計を行うプロフェッショナル向けの実装だ。
コピペしてそのまま現場のツールとして組み込んでほしい。エラーハンドリングとオブジェクトのクリーンアップ(ライフサイクル管理)も完璧に網羅している。
Option Explicit
‘ ==============================================================================
‘ 担当者必見:図面オブジェクトの高速属性フィルタリング&集計モジュール
‘ アーキテクチャ設計: チーフアーキテクト
‘ ==============================================================================
Public Sub ExecuteAdvancedFilter()
Dim acadApp As AcadApplication
Dim acadDoc As AcadDocument
Dim sSets As AcadSelectionSets
Dim sSet As AcadSelectionSet
‘ フィルタリング条件の定義(実務に合わせて変更してください)
Const TARGET_LAYER As String = “C-ANNO-TEXT”
Const TARGET_TYPE As String = “AcDbText” ‘ 例: 文字オブジェクト (AcDbMText なども可)
‘ 1. アプリケーションとアクティブドキュメントの安全な取得
On Error GoTo ErrorHandler
Set acadApp = ThisDrawing.Application
Set acadDoc = acadApp.ActiveDocument
‘ 2. 既存の同名選択セットが存在する場合は確実に破棄(メモリリーク防止の鉄則)
Set sSets = acadDoc.SelectionSets
On Error Resume Next
sSets.Item(“PracticalFilterSet”).Delete
On Error GoTo ErrorHandler
‘ 3. 新規選択セットの作成
Set sSet = sSets.Add(“PracticalFilterSet”)
‘ 4. DXFグループコードを用いた高速フィルタリングの構築
Dim filterType(0) As Integer
Dim filterData(0) As Variant
‘ グループコード 0: オジェクトタイプ(例: “LINE”, “CIRCLE”, “AcDbText” 等)
filterType(0) = 0
filterData(0) = TARGET_TYPE
‘ ※ 複数条件(画層+タイプなど)で絞り込む場合は、配列のサイズを拡張し
‘ グループコード 8 (画層名) などを追加します。
‘ 5. 選択セットに条件を満たすオブジェクトをバルクインポート
sSet.Select acSelectionSetAll, , , filterType, filterData
‘ 6. 結果の解析とロギング(AcadEntityとしての安全なキャスト)
Dim ent As AcadEntity
Dim matchCount As Long
matchCount = 0
Debug.Print “=== 属性フィルタリング処理開始 [条件: タイプ=” & TARGET_TYPE & “] ===”
If sSet.Count > 0 Then
For Each ent in sSet
‘ 追加のVBA側フィルタ(例:画層名での厳密な二次チェックが必要な場合)
If StrComp(ent.Layer, TARGET_LAYER, vbTextCompare) = 0 Then
matchCount = matchCount + 1
Debug.Print ” -> [Hit] Handle: ” & ent.Handle & ” / Layer: ” & ent.Layer & ” / Type: ” & ent.EntityName
‘ ここに個別オブジェクトに対するプロパティ変更やデータ取得処理を記述
‘ 例: ent.Color = acRed など
End If
‘ ループ内での明示的なオブジェクト解放はVBAの仕様上不要だが、
‘ 参照を保持し続けないようループ変数にNothingを代入する配慮もプロの技
Set ent = Nothing
Next ent
Else
Debug.Print ” -> 条件に一致するオブジェクトは存在しませんでした。”
End If
Debug.Print “=== 処理完了. 一致オブジェクト数: ” & matchCount & ” / ” & sSet.Count & ” (総スキャン数) ===”
‘ 7. クリーンアップ(リソースの解放:極めて重要)
sSet.Delete
Set sSet = Nothing
Set sSets = Nothing
Set acadDoc = Nothing
Set acadApp = Nothing
MsgBox “フィルタリングと集計が正常に完了しました。” & vbCrLf & _
“一致数: ” & matchCount & “件”, vbInformation, “自動化ツール”
Exit Sub
ErrorHandler:
‘ 異常系ハンドリング:確実にCOMリソースを解放してAutoCADのフリーズを防ぐ
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical, “致命的エラー”
‘ 確実なクリーンアップ
If Not sSet Is Nothing Then
On Error Resume Next
sSet.Delete
Set sSet = Nothing
End If
Set sSets = Nothing
Set acadDoc = Nothing
Set acadApp = Nothing
Err.Clear
End Sub
—
4. コードの解説と「プロの知見」
今回のコードには、単なる「動くコード」を超えた、実務を生き抜くための設計思想が組み込まれている。
① 選択セット名義の「二重登録バグ」の完全回避
AutoCADのVBAでは、一度作成した `SelectionSet` を明示的に削除しないまま同じ名前で作成しようとすると、容赦なくランタイムエラー(エラー番号 457: すでにこのキーはコレクションに存在します)が発生する。
これを防ぐため、`On Error Resume Next` を活用して確実に既存セットを削除・上書きする堅牢な構造にしている。
② DXFグループコードによる爆速ネイティブ検索
`For Each` で全ての図形を舐めるのではなく、`sSet.Select acSelectionSetAll, , , filterType, filterData` によって、AutoCAD内部のC++エンジンに検索を丸投げしている。このアプローチにより、数万個のエンティティが存在する巨大図面であっても、処理時間はコンマ数秒の世界に収まる。
③ 徹底的なメモリ・リソース管理(ライフサイクル管理)
AutoCAD VBAにおいて、`AcadSelectionSet` などのコレクションやオブジェクトの参照を残したままにすると、ドキュメントを閉じた後もメモリ上にプロセスが残り続け、PC全体のパフォーマンス低下やAutoCAD自体の不安定化を招く。
正常系・異常系(`ErrorHandler`)の双方で `Set xxx = Nothing` と `Delete` を徹底しているのはそのためだ。
—
5. データベースや外部ファイル連携への拡張性
このコードをベースにすれば、抽出した `AcadObject` のプロパティ(座標、長さ、面積、属性値など)を、そのまま SQLite や Excel(ADO経由)、あるいは CSV/JSON へシームレスに書き出すシステムへと容易にスケールさせることができる。
「図面データを制する者は、CAD業務を制する。」
ぜひこのコードをあなたの開発環境に導入し、圧倒的なパフォーマンスと堅牢性を体感してほしい。
