【極限のデータ衛生】図面データにおける「幽霊オブジェクト」の完全抹殺:Deep Purgeの実装戦略
CADデータ管理の現場において、未整理の図面(Dirty Drawing)を納品することは、技術者としての敗北を意味する。
どれほど精緻な設計がなされていても、そのDWGファイルの中に、使われていないレイヤー、定義のみが存在する線種、そして参照の断絶したブロック定義が蠢いているならば、それは「データの肥大化」という名の技術的負債をクライアントに押し付けているのと同義だ。ファイルサイズの増大は、ネットワーク転送の遅延を招き、大規模プロジェクトにおけるBIM/CIM連携のパフォーマンスを著しく低下させる。
本稿では、単なる `PURGE` コマンドの実行を超えた、オブジェクトの依存関係を解体し、メモリとファイル構造を極限までクリーンにする「Deep Purge」の実装ロジックを詳説する。
1. 依存関係のグラフ理論:なぜ一度のPurgeでは足りないのか
多くの初級者は、`SendCommand “-PURGE” “A” “*” “N”` を実行すれば事足りると誤解している。しかし、AutoCADのデータベース構造は、単一のフラットなリストではない。それは複雑な依存関係のグラフ(Dependency Graph)である。
例えば、以下の連鎖を想定してほしい。
1. ある「レイヤー」が、特定の「線種」を使用している。
2. その「線種」は、ある「ブロック定義」の中で参照されている。
3. その「ブロック」は、図面内の「図形(Entity)」によって参照されている。
このとき、図形を削除しただけでは、ブロックは消えない。ブロックが残っている限り、線種は消えない。線種が残っている限り、レイヤーは消えない。
真にクリーンな状態を実現するためには、「依存関係の末端から根源へ」、あるいは「依存関係が解消されるまで再帰的にパージを繰り返す」というアプローチが不可欠である。我々が実装すべきは、この再帰的クリーニングロジックだ。
2. VBAにおけるメモリ管理とオブジェクトのライフサイクル
VBA(COMインターフェース)を通じてAutoCADを操作する場合、最も注意すべきは参照カウント(Reference Counting)である。
VBA内でオブジェクト変数に図面要素を代入した瞬間、そのオブジェクトの参照カウントは上昇する。たとえ図面上でその要素を削除したとしても、VBAの変数に参照が残っている限り、AutoCADのデータベース上では「使用中」とみなされ、`PURGE` コマンドは失敗する。
「パージしたいのにパージできない」という問題の9割は、このVBA側でのオブジェクト解放漏れに起因する。
' 悪い例:参照が残っているためパージに失敗する
Dim ent As AcadEntity
For Each ent In ThisDrawing.ModelSpace
If ent.Layer = "UNUSED_LAYER" Then
ent.Delete
' ent = Nothing を忘れている、あるいはループ内で参照が保持されている
End If
Next
プロフェッショナルな実装では、オブジェクトを操作した直後に、明示的に `Set obj = Nothing` を行い、参照カウントを確実にゼロに落とすことが鉄則である。
3. 実装:Deep Purge & SaveAs モジュール
以下に、実務レベルで耐えうる、再帰的パージとメモリ最適化を組み込んだ実装例を示す。このコードは、単にコマンドを送るのではなく、オブジェクトの生存確認と依存関係の解消を念頭に置いている。
Option Explicit
' ==============================================================================
' Module: AdvancedCleanupManager
' Description: 図面内の未使用オブジェクトを再帰的に削除し、最適化された状態で保存する
' Author: Legendary Chief Architect
' ==============================================================================
Public Sub ExecuteDeepCleanupAndSave(ByVal targetPath As String)
Dim acadApp As AcadApplication
Dim doc As AcadDocument
On Error GoTo ErrorHandler
Set acadApp = ThisDrawing.Application
Set doc = ThisDrawing
Debug.Print "--- Cleanup Process Started: " & doc.Name & " ---"
' 1. グラフの断絶を解消するための再帰的パージ
' 依存関係が複雑な場合、1回のPurgeでは不十分なため、変化がなくなるまで繰り返す
Call PerformRecursivePurge(doc)
' 2. 未使用の文字スタイルや寸法スタイルの徹底排除
' (必要に応じて追加のパージロジックをここに集約)
' 3. 図面の監査 (Audit)
' パージ後のデータベース整合性を確認
Debug.Print "Running Audit..."
doc.Audit True
' 4. ファイルの保存 (SaveAs)
' バージョン互換性を考慮する場合、ここでSaveAsの引数を制御する
Debug.Print "Saving file to: " & targetPath
doc.SaveAs targetPath
Debug.Print "--- Cleanup Process Completed Successfully ---"
Exit Sub
ErrorHandler:
MsgBox "Critical Error during Cleanup: " & Err.Description, vbCritical, "System Error"
' ログ出力などの実務的なエラーハンドリングをここに実装
End Sub
Private Sub PerformRecursivePurge(ByRef doc As AcadDocument)
Dim purgeCount As Integer
Dim maxIterations As Integer
Dim currentIteration As Integer
maxIterations = 5 ' 無限ループ防止のためのセーフティガード
currentIteration = 0
Do
currentIteration = currentIteration + 1
' AutoCADのコマンドライン経由で強力なパージを実行
' -PURGE を使用し、ダイアログを抑制 (Command line mode)
' All, * (すべての名前), No (確認なし)
doc.SendCommand "-PURGE" & vbCr & "A" & vbCr & "*" & vbCr & "N" & vbCr
' 実行直後のメモリ解放を促すための待機(極めて微小な遅延)
DoEvents
' ここでは簡略化のため、パージが成功したかどうかの判定は
' コマンドの結果を直接取得できないため、反復回数で制御している。
' 本来は、ファイルサイズの変化や、オブジェクトカウントの減少を監視するのが理想。
If currentIteration >= maxIterations Then Exit Do
Loop While currentIteration < maxIterations ' 実務的には、パージの成否判定ロジックを組み込む
Debug.Print "Recursive Purge completed in " & currentIteration & " iterations."
End Sub
4. システム間連携における極限の知見:Windows API によるファイルロック制御
自動化システムを構築する際、最大の障壁となるのは「ファイルが別のプロセスによって使用中である」というランタイムエラーだ。
VBAで `SaveAs` を実行した後、そのファイルを即座に別のシステム(FTPアップローダーや、外部の解析エンジン)に渡そうとすると、AutoCADがファイルハンドルを解放しきれていないために、I/Oエラーが発生することがある。
これを回避するため、Windows APIを使用して、ファイルが物理的に「解放可能(Shared Modeで開けるか)」をチェックするロジックを組み込むのが、シニアエンジニアの流儀である。
' Windows APIの宣言
Private Declare PtrSafe Function CreateFile Lib "kernel32" Alias "CreateFileA" ( _
ByVal lpFileName As String, _
ByVal dwDesiredAccess As Long, _
ByVal dwShareMode As Long, _
ByVal lpSecurityAttributes As Long, _
ByVal dwCreationDisposition As Long, _
ByVal dwFlagsAndAttributes As Long, _
ByVal hTemplateFile As Long) As Long
Private Declare PtrSafe Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Private Const GENERIC_READ As Long = &H80000000
Private Const FILE_SHARE_READ As Long = &H1
Private Const OPEN_EXISTING As Long = 3
Private Const INVALID_HANDLE_VALUE As Long = -1
' ファイルがロックされているか確認する関数
Public Function IsFileLocked(ByVal filePath As String) As Boolean
Dim fileHandle As Long
fileHandle = CreateFile(filePath, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0)
If fileHandle = INVALID_HANDLE_VALUE Then
IsFileLocked = True ' ロックされている、またはアクセス不能
Else
CloseHandle fileHandle
IsFileLocked = False ' 解放されている
End If
End Function
この `IsFileLocked` を `SaveAs` の後にポーリング(一定間隔での再試行)として組み込むことで、堅牢な自動化パイプラインが完成する。
結論
図面データのクリーンアップは、単なる「掃除」ではない。それは、データの整合性を担保し、後続のプロセスに対する「予測可能性」を確保するための、極めて重要なエンジニアリングプロセスである。
オブジェクトのライフサイクルを理解し、依存関係のグラフを意識し、OSレベルのファイル制御まで視野に入れる。この視座の高さこそが、使い捨てのスクリプトと、真の業務自動化システムを分かつ境界線である。
