AutoCAD VBAを掌握する極限の知見:メモリ上の「ゴースト」を狩れ!`IsErased`プロパティによる図面破損の深層診断
こんにちは、チーフアーキテクトの私だ。
日夜、数万オブジェクトを内包する巨大な図面データの自動化や、CADインフラの構築に挑んでいる開発者なら、一度は直面したことがあるはずだ。
「なぜかプログラムが重い」
「図面を保存・再オープンすると要素が消える、あるいは致命的なエラー(Fatal Error)で落ちる」
「`.Delete`メソッドを叩いたはずなのに、オブジェクトがメモリ上に亡霊のように居座っている」
AutoCAD VBA初学者は、「`Delete`を呼べばその場でオブジェクトは消滅する」という幻想を抱く。しかし、AutoCADの内部アーキテクチャ(ObjectARXのメモリ管理機構)を理解している我々プロフェッショナルからすれば、それは甘い。
今回は、`AcadDocument.Database`の深層に潜り、メモリ上のゴミデータ(`IsErased = True`)を直接特定・駆逐するための極限のメモリ診断ロジックを伝授する。Undoスタックの呪縛から逃れ、真に健全な図面データベースを維持するための実践的アプローチを解説しよう。
—
1. なぜ「削除したはずのオブジェクト」がメモリに残るのか?
AutoCADのオブジェクト指向データベース(`AcadDatabase`)は、COMラッパーを通じて背後にC++の強固なオブジェクトモデルを持っている。
ここで重要な設計思想を一つ。
AutoCADにおいて、オブジェクトを削除する(`object.Delete`)とは、即座にメモリからバイナリが消し去られることを意味しない。それは単に「Erase(消去)フラグ」が立ち、データベースのインデックスから外れた状態になることを指す。
[通常の状態] —(Delete実行)—> [EraseフラグON] —(PURGE/保存 & 再読込)—> [メモリから完全消滅]
IsErased = False IsErased = True (ゴースト状態) 物理領域解放
この「Eraseフラグが立った状態のオブジェクト(ゴーストデータ)」が厄介な理由を挙げる。
1. Undo/Redoスタックの保持: ユーザーが「元に戻す」を実行した際、このオブジェクトを復活させる必要があるため、メモリ上にデータ構造が保持され続ける。
2. ポインタの迷子: 別の図形(例えばブロック参照やハッチングの境界)が、既に`IsErased = True`になったオブジェクトの句(Handle)を指し示し続けている場合、図面全体の整合性が崩れ、破損(Corruption)の原因となる。
3. イテレーションの負荷: `ModelSpace`などのコレクションをループするだけでは見えないが、`Database`の全エントリを直接走査すると、これらの「ゾンビ」が大量に発見されることがある。
業務効率化ツールを開発する際、このゴーストデータを放置したままバッチ処理などで図面を加工し続けると、メモリリークや予期せぬCOMエラー(「オブジェクトが閉じられています」等)の引き金になる。
—
2. 脆弱な設計 vs 堅牢な設計
❌ ありがちな非効率・危険なコード
以下のコードを見ただけで、現場のエンジニアなら冷や汗をかくはずだ。
‘ 【悪手】ModelSpaceを普通にループして削除を試みる
Dim ent As AcadEntity
For Each ent In ThisDrawing.ModelSpace
If ent.Layer = “TEMP_LAYER” Then
ent.Delete ‘ ここで削除しても、Database内にはゾンビが残る可能性
End If
Next ent
何が問題か?
- `ModelSpace`コレクション経由の走査は、既に削除マークがついたオブジェクトや、特殊なインデックスにあるオブジェクトを網羅できない。
- コレクションのループ中に要素を削除すると、インデックスがズレて予期せぬスキップが発生する(VBAでは運良く動くように見えても、内部で例外の温床になる)。
⭕ プロフェッショナルな設計アプローチ
Databaseオブジェクト(`ThisDrawing.Database`)を直接取得し、すべてのシンボルテーブルやブロックレコード、そしてモデルスペースの深部をイテレートする。さらに、明示的に`IsErased`プロパティを監視・集計し、必要であれば完全消滅(Purgeの代行や参照断ち切り)を行う。
—
3. 【プロダクションコード】メモリ上のゴーストを特定する診断エンジン
以下のコードは、現在の図面データベースをくまなくスキャンし、`IsErased = True`(削除済みだがメモリ上に残存している)なオブジェクトの総数、タイプ、およびハンドルをイミディエイトウィンドウに出力する堅牢な診断ツールである。
Option Explicit
”=============================================================================
” モジュール名: MdlDatabaseDiagnostics
” 概要: AutoCAD Database内の「IsErased = True」オブジェクトを徹底走査する
”=============================================================================
Public Sub DiagnoseErasedObjects()
Dim startTime As Double
startTime = Timer
On Error GoTo ErrorHandler
Dim db As AcadDatabase
Set db = ThisDrawing.Database
‘ 統計カウンター
Long TotalScanned As Long
Dim GhostCount As Long
TotalScanned = 0
GhostCount = 0
Debug.Print “====================================================”
Debug.Print ” AutoCAD Database 破損・ゴースト診断開始: ” & Now
Debug.Print ” 対象図面: ” & ThisDrawing.Name
Debug.Print “====================================================”
‘ Database内の全ブロック(ModelSpace, PaperSpace, ユーザー定義ブロック含む)を走査
Dim block As AcadBlock
For Each block In db.Blocks
‘ ブロックテーブルレコード自体のチェック
Call CheckAndLogEntity(block, TotalScanned, GhostCount)
‘ ブロック内のエンティティを走査
Dim ent As AcadEntity
For Each ent In block
Call CheckAndLogEntity(ent, TotalScanned, GhostCount)
Next ent
Next block
Debug.Print “====================================================”
Debug.Print ” 診断完了”
Debug.Print ” 総スキャン要素数: ” & TotalScanned
Debug.Print ” 検出されたゴースト (IsErased=True): ” & GhostCount
Debug.Print ” 処理時間: ” & Format(Timer – startTime, “0.00秒”)
Debug.Print “====================================================”
If GhostCount > 0 {
MsgBox “警告: メモリ上に ” & GhostCount & ” 件のゴーストデータ(削除済みオブジェクト)が検出されました。” & vbCrLf & _
“詳細はイミディエイトウィンドウを確認してください。”, vbExclamation, “Database診断警報”
Else {
MsgBox “診断終了: メモリ上の整合性は正常です。”, vbInformation, “Database診断正常”
}
Exit Sub
ErrorHandler:
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical, “致命的エラー”
End Sub
”—————————————————————————–
” 内部プロシージャ: 個別エンティティのIsErased状態を評価しログ出力
”—————————————————————————–
Private Sub CheckAndLogEntity(ByRef obj As Object, ByRef total As Long, ByRef ghosts As Long)
total = total + 1
On Error Resume Next
Dim isErasedStatus As Boolean
isErasedStatus = obj.IsErased
‘ プロパティ取得に失敗した場合はスキップ(破棄済みCOMオブジェクト等)
If Err.Number <> 0 Then
Err.Clear
Exit Sub
End If
On Error GoTo 0
If isErasedStatus Then
ghosts = ghosts + 1
‘ ゴーストデータの詳細情報をイミディエイトに出力
Debug.Print ” [ゴースト検出] ” & _
“Handle: ” & obj.Handle & ” | ” & _
“Type: ” & TypeName(obj) & ” | ” & _
“Layer: ” & GetObjectLayerSafely(obj)
End If
End Sub
”—————————————————————————–
” 安全なレイヤー名取得(一部のオブジェクトはLayerプロパティを持たないため)
”—————————————————————————–
Private Function GetObjectLayerSafely(ByRef obj As Object) As String
On Error Resume Next
GetObjectLayerSafely = “”
GetObjectLayerSafely = obj.Layer
If Err.Number <> 0 Then
GetObjectLayerSafely = “N/A (Layer属性なし)”
Err.Clear
End If
On Error GoTo 0
End Function
—
4. チーフアーキテクトからの実務アドバイス:この知見をどう活かすか?
このコードを現場のツールに組み込むことで、単なる「動くマクロ」から「プロフェッショナルなCADインフラツール」へとステップアップできる。
1. バッチ処理前の健康診断としての組み込み
深夜に走らせる図面クリーンアップ・バッチ(多数の図面を次々と開いて処理するスクリプト)の処理前と処理後にこの診断を挟みなさい。ゴーストが蓄積している図面を検知したら、一度強制的に`WBLOCK`でクリーンな環境にデータを書き出すか、パージを徹底するロジックへ誘導すべきだ。
2. 「オブジェクトが閉じられています」エラーの根絶
VBAで外部参照やブロック定義をいじっていて突然飛んでくるクラッシュの多くは、この`IsErased = True`になったオブジェクトに対してプロパティやメソッドを叩いてしまったことが原因である。常に操作の直前、あるいはコレクションのイテレーション時には `.IsErased` を確認する防御的プログラミング(Defensive Programming)を徹底すること。
AutoCADのメモリ構造を支配する者は、CAD自動化を制す。
単にコードを書くだけのプログラマーから、データベースの深淵まで見通すアーキテクトへ。今日のこの知見を、あなたの開発プロジェクトに直ちに実装してほしい。
