AutoCAD VBAを掌握する:属性(Attribute)一括処理における「メモリと構造」の極意
AutoCADのAPIを叩く際、多くのエンジニアが犯す過ちは「AutoCADをただのスクリプト実行エンジンと勘違いすること」だ。VBAはCOM(Component Object Model)という、現代のマネージド言語から見れば化石のような層の上に成り立っている。
大規模図面で「属性の一括置換が遅い」「メモリリークでAutoCADが落ちる」と嘆く前に、君が扱っているのが「オブジェクトのポインタ」であり、その背後にはCOMの参照カウントがあることを理解せねばならない。
今日は、属性(Attribute)の抽出と更新を題材に、システム管理者レベルの視点から「真の効率化」を説く。
—
1. 属性抽出のボトルネック:再帰的探索とCOMのオーバーヘッド
ブロック参照(`AcadBlockReference`)は、内部に`GetAttributes`というメソッドを持っている。しかし、これを漫然と呼び出してはならない。
大規模図面では、モデル空間だけでなくペーパー空間、さらにはブロック定義内(ネストされたブロック)まで探る必要がある。ここで重要なのは、「Variant型による配列の受け渡し」を最小化することだ。
‘ 【極意】属性抽出の最適化
‘ GetAttributesで返されるのはバリアント配列。これの処理をループ内で行うと
‘ メモリの断片化を招く。必要な情報だけを抽出する設計にする。
Public Sub ExportAttributesToCSV(ByVal filePath As String)
Dim ent As AcadEntity
Dim blkRef As AcadBlockReference
Dim atts As Variant
Dim i As Long
Dim fileNum As Integer
fileNum = FreeFile
Open filePath For Output As #fileNum
‘ モデル空間の走査
For Each ent In ThisDrawing.ModelSpace
If TypeOf ent Is AcadBlockReference Then
Set blkRef = ent
If blkRef.HasAttributes Then
atts = blkRef.GetAttributes ‘ ここでメモリ確保が発生
For i = LBound(atts) To UBound(atts)
‘ 書き込み処理
Print #fileNum, blkRef.Name & “,” & atts(i).TagString & “,” & atts(i).TextString
Next i
‘ 【重要】明示的なメモリ解放(VBAではNothing代入が必須)
Erase atts
End If
End If
Next ent
Close #fileNum
End Sub
2. 属性一括置換:更新の「不可逆的リスク」を管理する
属性値を書き換える際、単に `.TextString` を代入するだけで満足していないだろうか。AutoCADの属性更新は、表示上の同期(`Update`メソッド)と、図面データベース内の同期が微妙にズレることがある。
特に、「属性が定義されていないのに、ブロック参照側には存在している(あるいはその逆)」という汚れたデータに遭遇した場合、VBAは容易に例外を吐く。
シニアエンジニアの流儀:エラーハンドリングと更新のフラッシュ
COMの参照を保持したまま、別の処理に移行するとデッドロックやメモリリークの原因になる。処理終了時には必ず `Set object = Nothing` を徹底する。これは宗教ではなく、COMの生存期間を制御するための防衛策だ。
Public Sub UpdateAttributeValue(ByVal targetTag As String, ByVal newValue As String)
Dim ent As AcadEntity
Dim blkRef As AcadBlockReference
Dim atts As Variant
Dim i As Long
For Each ent In ThisDrawing.ModelSpace
If TypeOf ent Is AcadBlockReference Then
Set blkRef = ent
If blkRef.HasAttributes Then
atts = blkRef.GetAttributes
For i = LBound(atts) To UBound(atts)
If atts(i).TagString = targetTag Then
atts(i).TextString = newValue
atts(i).Update ‘ 図形表示を強制リフレッシュ
End If
Next i
Erase atts
End If
End If
Set blkRef = Nothing ‘ ループごとのクリーンアップ
Next ent
End Sub
—
3. レガシー環境とWindows API:極限のチューニング
もし君が数万個のブロックを処理しなければならないなら、VBAの単一スレッド処理は限界を迎える。その場合、Windows APIを使ってAutoCADのウィンドウメッセージを制御し、描画を一時停止することで、処理速度を劇的に向上させることが可能だ。
- `LockWindowUpdate`: AutoCADの再描画を抑制する。
- `DoEvents`: 応答なし判定を防ぐが、やりすぎるとパフォーマンスが落ちる。500要素ごとに叩くのが黄金比だ。
‘ API宣言の例
Private Declare PtrSafe Function LockWindowUpdate Lib “user32” (ByVal hWndLock As LongPtr) As Long
‘ 実行時の最適化
LockWindowUpdate ThisDrawing.hWnd
‘ … 大量処理 …
LockWindowUpdate 0 ‘ 解除
—
結論:コードの先にあるもの
AutoCAD VBAを極めるということは、単にメソッド名を知ることではない。「AutoCADの背後で、どの程度のメモリが動いており、どの程度の描画負荷が掛かっているか」を脳内でシミュレーションすることだ。
属性情報の抽出は、単なる事務作業ではない。図面データという「非構造化データ」を「構造化された資産」に変えるゲートウェイである。
君の書くコードは、単なるツールではない。組織の図面管理のインフラそのものだ。メモリとオブジェクトのライフサイクルを慈しみ、妥協のないコードを書き続けてほしい。
それが、伝説のアーキテクトに近づく唯一の道だ。
