AutoCAD VBAを掌握する極限の知見:図面オブジェクトへのカスタムプロパティ動的付与と永続化のアーキテクチャ
AutoCAD VBAにおいて、単なる図形の描画自動化は初級の領域に過ぎない。真にエンタープライズなシステムを構築する場合、避けて通れないのが「図面内の幾何オブジェクトと外部データの紐付け(メタデータの永続化)」である。
多くの開発者が、外部のExcelやデータベース(DB)にAutoCADの「Handle」や「ObjectID」を保存し、それをキーにして情報を引き当てようとする。しかし、このアプローチは図面のコピー、ブロック化、あるいは他者による編集によって一瞬で破綻する。オブジェクトのライフサイクルとAutoCADの内部データベース構造を理解していない証拠だ。
今回は、AutoCADの隠れた強力な機能である 「Xrecord(拡張レコード)」 と 「Dictionary(辞書)」 をVBAから完璧に制御し、図面内の任意のオブジェクトに堅牢なカスタムプロパティを動的に付与・管理するプロフェッショナル向けソリューションを提示する。
—
1. AutoCADオブジェクトモデルの深層:なぜ「通常の変数」ではダメなのか
VBAのクラスモジュールでオブジェクトに独自プロパティを持たせても、それはメモリ上の揮発性データにすぎず、図面を保存して閉じれば消滅する。さらに、AutoCADのオブジェクト(`AcadEntity`など)自体に、ユーザー定義のプロパティを直接追加するネイティブな拡張領域は存在しない。
ここで登場するのが、AutoCADのデータベース構造(ObjectDBX)の根幹を成す ExtensionDictionary(拡張辞書) である。
すべての `AcadEntity` は、自分自身に紐づくプライベートな `Dictionary` を保持する能力を持っている。この辞書の中に `Xrecord`(バイナリデータをキーバリュー形式で格納できるオブジェクト)を格納することで、図面ファイル(.dwg)の内部にメタデータを完全に封印・永続化できる。
[AcadDocument]
└─ [AcadEntity (Line/Circle etc.)]
└─ [ExtensionDictionary]
└─ [Xrecord (カスタムプロパティのコンテナ)]
├─ DXF 1: “材質” (文字列)
└─ DXF 2: “管理番号” (文字列)
このアーキテクチャを採用すれば、オブジェクトが移動しようが、画層を変更されようが、メタデータはオブジェクトと運命を共にする。
—
2. 実装:堅牢なカスタムプロパティ管理エンジン
以下に、指定したオブジェクトに対してカスタムプロパティ(例: “材質” と “管理番号”)をアタッチし、読み書きを行う実用的なVBAコードを示す。
メモリリークを防ぐためのオブジェクトの明示的な解放(`Nothing`代入)や、エラーハンドリングの作法もシニアエンジニアの基準で実装している。
Option Explicit
‘ ==============================================================================
‘ モジュール名: clsCustomPropertyManager
‘ 概要: AutoCADオブジェクトに対する拡張辞書(Xrecord)を用いたプロパティ管理
‘ ==============================================================================
‘ 定数定義
Private Const DICTIONARY_NAME As String = “EnterpriseDataDictionary”
Private Const XRECORD_NAME As String = “CustomProperties”
Public Sub SetCustomProperties(ByRef targetEntity As AcadEntity, ByVal material As String, ByVal mgmtNumber As String)
Dim extDict As AcadDictionary
Dim xRec As AcadXrecord
Dim dataTypes(0 To 1) As Integer
Dim dataValues(0 To 1) As Variant
On Error GoTo ErrorHandler
‘ 1. オブジェクトが有効か検証
If targetEntity Is Nothing Then Exit Sub
‘ 2. 拡張辞書の取得、または存在しない場合は新規作成
On Error Resume Next
Set extDict = targetEntity.GetExtensionDictionary
If extDict Is Nothing Then
‘ 拡張辞書を持てない特殊なオブジェクトへの対策
Err.Raise 9999, “SetCustomProperties”, “このオブジェクトには拡張辞書を付与できません。”
End If
On Error GoTo ErrorHandler
‘ 3. 既存のXrecordが存在するか確認し、あれば削除(上書き更新のため)
On Error Resume Next
Set xRec = extDict.Item(XRECORD_NAME)
If Not xRec Is Nothing Then
xRec.Delete
Set xRec = Nothing
End If
On Error GoTo ErrorHandler
‘ 4. 新規Xrecordの作成
Set xRec = extDict.AddXrecord(XRECORD_NAME)
‘ 5. 格納するデータの設定 (DXFグループコード 擡頭: 1 = 文字列)
dataTypes(0) = 1: dataValues(0) = material
dataTypes(1) = 1: dataValues(1) = mgmtNumber
‘ 6. データをXrecordに書き込み
xRec.SetXRecordData dataTypes, dataValues
CleanUp:
‘ 【重要】COMオブジェクトの参照解放によるメモリ最適化
Set xRec = Nothing
Set extDict = Nothing
Exit Sub
ErrorHandler:
MsgBox “カスタムプロパティの設定中にエラーが発生しました: ” & Err.Description, vbCritical
Resume CleanUp
End Sub
Public Sub GetCustomProperties(ByRef targetEntity As AcadEntity, ByRef material As String, ByRef mgmtNumber As String)
Dim extDict As AcadDictionary
Dim xRec As AcadXrecord
Dim dataTypes As Variant
Dim dataValues As Variant
On Error GoTo ErrorHandler
material = “”
mgmtNumber = “”
If targetEntity Is Nothing Then Exit Sub
‘ 1. 拡張辞書の取得(存在しない場合はエラーになるためトラップ)
On Error Resume Next
Set extDict = targetEntity.GetExtensionDictionary
If extDict Is Nothing Then GoTo CleanUp
Set xRec = extDict.Item(XRECORD_NAME)
If xRec Is Nothing Then GoTo CleanUp
On Error GoTo ErrorHandler
‘ 2. Xrecordからデータを抽出
xRec.GetXRecordData dataTypes, dataValues
‘ 3. 配列の安全性を確認して値を取り出し
If IsArray(dataValues) Then
If UBound(dataValues) >= 1 Then
material = CStr(dataValues(0))
mgmtNumber = CStr(dataValues(1))
End If
End If
CleanUp:
Set xRec = Nothing
Set extDict = Nothing
Exit Sub
ErrorHandler:
MsgBox “カスタムプロパティの取得中にエラーが発生しました: ” & Err.Description, vbCritical
Resume CleanUp
End Sub
—
3. 実践:図面全体のスキャンとバッチ処理の統合
上記のクラス(または標準モジュール)をベースに、図面内にあるすべての円(`AcadCircle`)を走査し、まだカスタムプロパティを持っていないものに対して自動的に初期値を付与するバッチ処理の例を示す。
Public Sub BatchAssignPropertiesToCircles()
Dim acadDoc As AcadDocument
Dim ent As AcadEntity
Dim mat As String
Dim num As String
Dim counter As Long
Set acadDoc = ThisDrawing
counter = 0
‘ 処理の高速化のためのトランザクション的思考(画面描画のロック)
acadDoc.Utility.Prompt “カスタムプロパティのバッチ処理を開始します…” & vbCrLf
For Each ent In acadDoc.ModelSpace
If TypeOf ent Is AcadCircle Then
‘ 既存のプロパティをチェック
GetCustomProperties ent, mat, num
‘ 未設定の場合のみ自動付与
If mat = “” Then
Call SetCustomProperties(ent, “SUS304”, “MGM-2023-” & Format(counter + 1, “0000”))
counter = counter + 1
End If
End If
Next ent
acadDoc.Utility.Prompt “処理完了: ” & counter & “件のオブジェクトにカスタムプロパティを付与しました。” & vbCrLf
‘ オブジェクトの解放
Set ent = Nothing
Set acadDoc = Nothing
End Sub
—
4. チーフアーキテクトからの警鐘:メモリ管理とレガシー環境の罠
AutoCAD VBAを実務レベル、特に数百MBに及ぶ巨大なプラント図面や建築図面で運用する場合、以下の「プロの鉄則」を厳守しなければならない。
① COMオブジェクトの即時解放(Garbage Collectionの補完)
VBAのランタイムは、AutoCADのCOMオブジェクト(`AcadEntity`, `AcadDictionary`, `AcadXrecord`など)が生成するメモリの解放に関して非常にルーズである。特に `For Each` ループ内でオブジェクトを変数に格納し続けると、VBAの内部参照カウンタが肥大化し、最悪の場合はAutoCAD自体がメモリ不足(Fatal Error)でクラッシュする。
- 対策: ループ内で使用した変数には、処理の都度、あるいはプロセスの最後に必ず `Set 変数 = Nothing` を明示的に記述せよ。
② AutoLISP / .NET (ObjectARX) との共存
VBAから付与したXrecordは、AutoLISP(`dictsearch`関数など)や、C# / VB.NETで書かれたAutoCAD .NET APIからも完全に読み書き可能である。将来的にシステムを.NETに移行するロードマップを描いている場合でも、このXrecordベースの設計にしておけば、データ構造を一切変更することなく、スムーズにAPIをリプレイスできる。レガシーなVBA投資を無駄にしないための最良の保険となる。
③ エラーハンドリングと「サイレント・フェイル」の排除
図面データは日々さまざまなユーザーによって改変される。予期せぬレイヤーロック、ブロックの入れ子構造などにより、拡張辞書へのアクセスが失敗することは日常茶飯事である。
- `On Error Resume Next` を使用した後は、必ず適切なスコープで `On Error GoTo 0` またはエラーログの出力を挟み、エラーを握りつぶさない堅牢なコードベースを維持すること。
—
総括
図面の中に「データ」を宿す。このアプローチをマスターした瞬間、AutoCAD VBAは単なる「お絵描きマクロ」から、真の「エンジニアリング・データベース・クライアント」へと進化する。
仕様の限界を嘆く前に、AutoCADの底流にあるObjectDBXのアーキテクチャを理解し、手元のコードを研ぎ澄ませ。それこそが、現場を圧倒するシニアエンジニアの仕事である。
