AutoCAD VBAを掌握する極限の知見
第1回:AcadDocument.Database.Groupsコレクションによる「論理的な図形グループ」の自動生成と管理
開発プロジェクトのリーダーである私たちが、日常の設計業務や図面データのCIM/CAD連携において最も直面するストレスの一つ。それは、「バラバラの図形を一時的に、かつ論理的に束ねて一括操作したい」という要求に対し、安易にブロック定義(Block Table Record)を切ってしまうことによる「データ資産の肥大化と管理コストの爆発」だ。
ブロックは強力だが、個々の図形のプロパティをその場で直感的にいじりたい、あるいは一時的なアソシエーション(関連付け)を動的に組み替えたいという要件に対しては、あまりに重く、硬直したアーキテクチャと言わざるを得ない。
そこで活用すべきなのが、`AcadDocument.Database.Groups` コレクションである。
ブロック化の足枷(名前付きブロックの乱立、分解の手間)を一切排除し、図形(AcadEntity)の本質的なポインタをそのままに「論理的なグループ」として束ねるこの仕組みを、VBAから極限まで堅牢に操る方法を伝授しよう。
—
1. なぜ「ブロック」ではなく「グループ」なのか?
実務の現場でありがちなアンチパターンとして、「複数の注記と引出し線を一つの塊として移動させたいから」という理由だけで、図面内に無数の無名ブロック(Anonymous Block)を量産する開発者がいる。
これはデータベースの観点から見ると悪夢だ。
- ブロック定義テーブルが汚染され、図面ファイルサイズ(.dwg)が肥大化する。
- 個別の図形の色や画層を動的に一括変更したいとき、ブロックエディタを開くか、属性定義の書き換えという不毛なコードを書く羽目になる。
グループ(AcadGroup)の本質は「ポインタのリスト」にすぎない。
実体はモデル空間やペーパー空間に存在する個々の図形のままであり、データベース上の `Groups` コレクションに「この図形とこの図形はセットである」というインデックス登録を行うだけである。そのため、軽量であり、プログラムからの動的なメンバー追加・削除が極めて高速に行える。
—
2. 堅牢なグループ操作におけるアーキテクチャの鉄則
VBAから `Groups` を操作する際、プログラマが必ずハマる「地雷」がいくつか存在する。これらを回避するための設計思想を共有する。
1. 同名グループの衝突(Collision)ハンドリング
AutoCADのデータベース内では、同一グループ名の存在は許容されない。既存のグループ名に新規作成を試みると、容赦なくランタイムエラー(Err.Number = -2145386446 等)が発生する。生成前に必ず存在確認、あるいは既存破棄のロジックを挟むこと。
2. ObjectID配列の型安全性
`Group.AppendItems` メソッドに渡す引数は、「AcadEntityのObjectIDを格納したVariant(Longの1次元配列)」でなければならない。ここをサボると、型不一致エラーの無限ループに陥る。
3. オブジェクトのライフサイクルとエラー対策
グループに含まれる図形がユーザーによって個別に削除された場合、グループ内のポインタは「dangling pointer(宙ぶらりんの参照)」になり得る。そのため、グループを操作する前には必ず図形の存在確認(IsValid等)を行う防御的プログラミングが必須となる。
—
3. 【プロダクションコード】堅牢なグループ自動生成と管理モジュール
以下のコードは、選択した複数の図形(または特定の条件に合致する図形)を動的にグループ化し、一括で操作・管理するための実践的なVBAモジュールである。
プロジェクトにそのまま貼り付け、実務の自動化パイプラインに組み込んでほしい。
Option Explicit
” =================================================================
” モジュール名: mGroupManager
” 概要: AcadDocument.Database.Groups を用いた論理グループの高度管理
” 開発現場の知見に基づくエラーハンドリング実装版
” =================================================================
Public Sub CreateAndManageLogicalGroup()
Dim acadDoc As AcadDocument
Set acadDoc = ThisDrawing.Application.ActiveDocument
‘ 1. ユーザーに図形を選択させる(またはプログラム側でフィルタリング)
Dim sset As AcadSelectionSet
Set sset = CreateSafeSelectionSet(acadDoc, “TEMP_GROUP_SS”)
On Error GoTo ErrorHandler
MsgBox “グループ化する図形を選択してください。”, vbInformation, “グループ作成”
sset.SelectOnScreen
If sset.Count = 0 Then
MsgBox “図形が選択されませんでした。処理を中断します。”, vbExclamation
ExitSub:
Call CleanupSelectionSet(acadDoc, “TEMP_GROUP_SS”)
Exit Sub
End If
‘ 2. 選択された図形からObjectIDの配列を抽出
Dim i As Long
Dim entCount As Long
entCount = sset.Count
ReDim entityIDs(0 To entCount – 1) As Long
For i = 0 To entCount – 1
entityIDs(i) = sset.Item(i).ObjectID
Next i
‘ 3. グループ名の定義と重複チェック・既存削除
Dim groupName As String
groupName = “AutoCAD_VBA_Managed_Group_” & Format(Now, “yyyymmdd_hhnnss”)
Dim targetGroup As AcadGroup
Set targetGroup = GetOrCreateGroup(acadDoc, groupName)
‘ 4. グループへメンバーを追加
‘ ※注意: 既にグループに属しているObjectIDを追加するとエラーになるため、一度クリアする
If targetGroup.Count > 0 Then
targetGroup.RemoveItems entityIDs
End If
targetGroup.AppendItems entityIDs
‘ 5. 動作確認:グループを選択状態にしてハイライト
targetGroup.Highlight
MsgBox “グループ [” & groupName & “] を正常に生成し、” & entCount & “個の図形を格納しました。”, vbInformation, “成功”
targetGroup.Unhighlight
GoTo ExitSub
ErrorHandler:
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical, “致命的エラー”
Resume ExitSub
ExitSub:
Call CleanupSelectionSet(acadDoc, “TEMP_GROUP_SS”)
End Sub
” =================================================================
” 補助関数: グループの安全な取得または新規作成
” =================================================================
Private Function GetOrCreateGroup(doc As AcadDocument, gName As String) As AcadGroup
Dim grp As AcadGroup
Dim isExists As Boolean
isExists = False
‘ 既存のGroupsコレクションを走査
Dim g As AcadGroup
For Each g in doc.Database.Groups
If StrComp(g.Name, gName, vbTextCompare) = 0 Then
Set grp = g
isExists = True
Exit For
End If
Next g
‘ 存在しない場合は新規作成
If Not isExists Then
Set grp = doc.Database.Groups.Add(gName)
End If
Set GetOrCreateGroup = grp
End Function
” =================================================================
” 補助関数: 選択セットの安全な生成(既存重複エラーの回避)
” =================================================================
Private Function CreateSafeSelectionSet(doc As AcadDocument, ssName As String) As AcadSelectionSet
Dim ss As AcadSelectionSet
‘ 既存の同名選択セットがあれば削除
On Error Resume Next
Set ss = doc.SelectionSets.Item(ssName)
If Not ss Is Nothing Then
ss.Delete
End If
On Error GoTo 0
‘ 新規作成
Set CreateSafeSelectionSet = doc.SelectionSets.Add(ssName)
End Function
” =================================================================
” 補助関数: 選択セットのクリーンアップ
” =================================================================
Private Sub CleanupSelectionSet(doc As AcadDocument, ssName As String)
On Error Resume Next
Dim ss As AcadSelectionSet
Set ss = doc.SelectionSets.Item(ssName)
If Not ss Is Nothing Then
ss.Delete
End If
On Error GoTo 0
End Sub
—
4. プロフェッショナルとしての実装上の注意点
このコードベースを実務の巨大な図面(数十万オブジェクトが混在するインフラ図面やプラント図面など)に適用する際、以下のアーキテクチャ上の知見を忘れてはならない。
- パフォーマンスの最適化:
数千の図形を `Groups.Add` や `AppendItems` する際、AutoCADの画面描画(Graphics Window)が有効なままだと、1オブジェクト追加ごとに画面が再描画され、実行時間が致命的に遅延する。大量処理を行う前には必ず `doc.Application.ZoomExtents` などの重い処理を避け、必要に応じて `AcadApplication.ActiveDocument.Utility` 等で画面更新の抑制を検討すること(※VBA単体では完全な画面描画停止は困難なため、不要な選択セットの走査を最小限に抑えることが最大の最適化となる)。
- データベース連携(Excel/Access/DB)との親和性:
グループに対して「外部データベースのレコードID(Primary Key)」を紐付けたい場合、AutoCADの `XData(拡張データ)` または `Dictionary(拡張辞書)` をグループオブジェクト自体に付与するのが定石である。これにより、「どの外部データが、どの論理グループ図形群と紐づいているか」を完全にトレース可能なCIM/CADシステム構築の第一歩となる。
ブロックに逃げるな。論理の力で図面を支配せよ。
この `Groups` コレクションの掌握こそが、君のAutoCAD自動化スクリプトを「おもちゃのマクロ」から「プロフェッショナルなエンタープライズツール」へと昇華させる鍵となる。
