AutoCAD VBAを掌握する極限の知見:`AcadGroups` コレクションによる論理的グループの高度自動制御
AutoCAD VBAの開発現場において、多くのプログラマは `AcadBlock` や `AcadSelectionSet` の扱いに終始しがちだ。しかし、実務の現場――特にプラント設計、建築意匠、大規模な回路図面などの動的な編集が求められる領域において、ブロック化の「非可逆性」や、セレクションセットの「一過性」にフラストレーションを溜めた経験はないだろうか。
「個々の図形の独立性を維持したまま、ユーザーが単一のオブジェクトであるかのように直感的に選択・操作できるようにしたい」
このエンジニアリングの要求に対するAutoCADの回答が、`Database.Groups` コレクション、すなわち「論理的グループ(AcadGroup)」である。今回は、この強力かつ過小評価されている機能をVBAから極限まで制御し、メモリ管理とパフォーマンスの限界に挑むための実践的知見を公開する。
—
1. オブジェクトモデルの深層:Blocks, SelectionSets, そして Groups
まず、アーキテクチャの正確な理解から始めよう。AutoCADの図形管理において、これら3つの概念は根本的にライフサイクルとメモリ上の存在意義が異なる。
| 概念 | 格納場所 | 永続性 | 編集の柔軟性 | 用途の最適解 |
| :— | :— | :— | :— | :— |
| AcadBlock | `Blocks` | 永続(図面内) | 低(定義の書き換えが必要) | 共通パーツのインスタンス化 |
| AcadSelectionSet | メモリ(一時的) | 不揮発(セッション限定) | 中 | 検索・一括処理のバッファ |
| AcadGroup | `Database.Groups` | 永続(図面内) | 高(構成要素の個別編集が容易) | 関連図形の動的アソシエーション |
`AcadGroup` の本質は、「データベースレベルで図形のポインタ配列を名前付きで保持する論理コンテナ」に過ぎない。ブロックのように新しい図形定義(Block Table Record)を生成せず、既存のエンティティ(`AcadEntity`)のハンドル(Handle)またはオブジェクト ID の参照関係を束ねるだけであるため、オーバーヘッドが極めて少ないのが特徴だ。
—
2. 実装:堅牢なグループ自動生成と例外処理のパターン
VBAから `Groups` コレクションを操作する際最大の罠となるのが、「同名グループの重複作成による実行時エラー」と、「存在しないエンティティの追加によるクラッシュ」である。
シニアエンジニアたる者、エラーハンドリングは単なる `On Error Resume Next` の乱用ではなく、トランザクション的な安全性と状態のクリーンアップを伴わなければならない。
以下のコードは、指定された複数のエンティティを安全にグループ化し、既存の同名グループが存在する場合は自動的にパージ(削除)して再構築する実用的なプロシージャである。
Option Explicit
‘ ==============================================================================
‘ 業務自動化アーキテクチャ: 高度グループ管理モジュール
‘ ==============================================================================
Public Sub CreateManagedGroup(ByVal groupName As String, ByRef targetEntities() As AcadEntity)
Dim acadApp As AcadApplication
Dim acadDoc As AcadDocument
Dim targetGroup As AcadGroup
Dim i As Long
Dim entCount As Long
‘ 1. アプリケーションおよびドキュメントコンテキストの安全な取得
On Error GoTo ErrorHandler
Set acadApp = ThisDrawing.Application
Set acadDoc = acadApp.ActiveDocument
‘ 2. 配列の有効性検証 (Null配列・空配列のガード)
If Not IsArrayAllocated(targetEntities) Then
Err.Raise vbObjectError + 1000, “CreateManagedGroup”, “ターゲットエンティティ配列が初期化されていません。”
End If
entCount = UBound(targetEntities) – LBound(targetEntities) + 1
If entCount = 0 Then Exit Sub
‘ 3. 既存グループの存在確認と安全な削除 (Idempotencyの確保)
Dim grp As AcadGroup
On Error Resume Next
Set grp = acadDoc.Database.Groups.Item(groupName)
On Error GoTo ErrorHandler
If Not grp Is Nothing Then
‘ 既存グループが存在する場合、グループ定義のみを削除(構成図形は消さない)
grp.Delete
Set grp = Nothing
End If
‘ 4. 新規グループの生成
Set targetGroup = acadDoc.Database.Groups.Add(groupName)
‘ 5. エンティティのバッチアタッチ
‘ AcadGroup.AppendItems は Variant配列を受け取る
Dim entHandles() As AcadEntity
ReDim entHandles(0 To entCount – 1)
For i = 0 To entCount – 1
Set entHandles(i) = targetEntities(LBound(targetEntities) + i)
Next i
targetGroup.AppendItems entHandles
‘ 6. 成功ログとステータス更新
acadDoc.Utility.Log “Group [” & groupName & “] successfully created with ” & entCount & ” entities.”
GoTo CleanUp
ErrorHandler:
MsgBox “グループ作成中に致命的なエラーが発生しました: ” & Err.Description, vbCritical, “AutoCAD VBA Architecture”
CleanUp:
‘ 7. オブジェクト参照の明示的解放 (メモリリークの根絶)
Set targetGroup = Nothing
Set grp = Nothing
Set acadDoc = Nothing
Set acadApp = Nothing
End Sub
‘ — ヘルパー関数: 配列の割当状態判定 —
Private Function IsArrayAllocated(ByRef arr() As AcadEntity) As Boolean
On Error Resume Next
IsArrayAllocated = Not CBool(Err.Number) And Not (Ubound(arr) < Lbound(arr))
On Error GoTo 0
End Function
---
3. パフォーマンス最適化とメモリライフサイクルの極意
AutoCAD VBAで大規模図面を扱う際、最大の敵はCOMコンポーネント間の境界を越えるmarshalling(マーシャリング)のオーバーヘッドと、循環参照によるメモリリークである。
オブジェクト変数の「即時無効化(Nullification)」
VBAのガベージコレクションは参照カウント方式(Reference Counting)に依存している。`AcadDocument` や `AcadDatabase`、特にコレクション(`Groups` や `SelectionSets`)への参照をコードブロックの終了時に `Set obj = Nothing` で明示的に解放しないと、CADセッション内にCOMラッパーが残留し、図面を閉じた際やアドインをアンロードした際にメモリリーク(メモリフットプリントの肥大化)を引き起こす。
特に `Groups.Add` や `Groups.Item` をループ内で多用する場合、ローカル変数のスコープを極限まで小さくし、明示的解放を徹底することが、数万オブジェクトを扱うシステムにおけるフリーズを防ぐ唯一の手段となる。
—
4. レガシー環境とシステム間連携(外部DB・Excel連携)への応用
この `AcadGroup` の仕組みが真価を発揮するのは、外部データベース(BOM/部品表やGISデータ)とAutoCAD図面を同期させるシステム連携の場面である。
例えば、ERPシステムから発番された「ユニークなアセンブリID」をAutoCAD上の図形群に紐付ける場合、`AcadGroup.Name` にそのIDをそのまま割り当てる。これにより、VBA側から以下のようなクエリ的なアプローチが可能になる。
‘ 外部IDから対応するグループを即座に取得し、含まれる図形の色をハイライトする一括処理
Public Sub HighlightAssemblyGroup(ByVal assemblyID As String)
Dim acadDoc As AcadDocument
Dim targetGroup As AcadGroup
Dim ent As AcadEntity
Set acadDoc = ThisDrawing.Application.ActiveDocument
On Error Resume Next
Set targetGroup = acadDoc.Database.Groups.Item(assemblyID)
On Error GoTo 0
If targetGroup Is Nothing Then
MsgBox “指定されたアセンブリIDのグループが見つかりません: ” & assemblyID, vbExclamation
Exit Sub
End If
‘ グループ内の全エンティティを走査し、プロパティを一括変更
For Each ent In targetGroup
ent.Color = acRed ‘ 例として赤色に変更
ent.Update
Next ent
‘ クリーンアップ
Set targetGroup = Nothing
Set acadDoc = Nothing
End Sub
この手法を用いれば、複雑な座標計算や空間検索(Spatial Indexing)を行うことなく、業務ロジックのキーと図面上の塊を1対1で直結させることができる。
—
5. チーフアーキテクトからの提言
`AcadDocument.Database.Groups` は、単なる「図形をまとめる便利機能」ではない。それは、CADという幾何学的空間の中に、業務上の「意味論(Semantics)」を持ち込むための強力なアーキテクチャである。
ブロック化によるファイルの肥大化を避け、セレクションセットの揮発性に悩まされない堅牢なソリューションを構築したいのであれば、今すぐコードベースにこの `AcadGroup` の制御層を組み込むべきだ。
メモリのライフサイクルを支配し、オブジェクトの参照を完全に掌握した者だけが、AutoCAD VBAの真の限界を突破することができる。妥協なきコードで、次の自動化の領域へ踏み出してほしい。
