AutoCAD VBAを掌握する極限の知見
【第4回】図面を開く前に、図面内のオブジェクト数をカウントし、処理負荷を予測
多くのVBAエンジニアは、図面内のデータを解析するために `Documents.Open` を実行し、画面上に図面を描画させてから処理を試みる。これは大規模図面のバッチ処理において、最も避けるべき致命的なアンチパターンである。
グラフィックスパイプラインの初期化、メモリへの全エンティティの展開、イベントハンドラのバインド。これらはバックグラウンド処理において完全に無駄なオーバーヘッドであり、数百ファイルのバッチ処理を実行すれば、メモリリークとGC(ガベージコレクション)の圧迫により、確実にAutoCADはクラッシュする。
真に洗練された自動化アーキテクチャとは、「図面を開かずに(No-UI / Silent)、メタデータのみからリソースコストを算出し、処理の可否や優先度を動的に制御する」ことにある。
本稿では、AutoCADの内部データベース構造(DWGファイルのバイナリヘッダおよびCOMコンポーネントの極限利用)をハックし、図面を開かずにオブジェクト総数を秒速で割り出す実務コードと、その設計思想を解説する。
—
1. なぜ「開かずに数える」のか?:アーキテクチャの極限最適化
AutoCADのDWGファイルは、単なる図面データではなく、一種のオブジェクト指向データベースである。ファイルサイズ(KB/MB)だけで処理負荷を予測するのは極めて危険だ。例えば、3MBの図面であっても、それが高精度な3Dソリッドや、数百万のハッチング境界セグメントで構成されていれば、処理負荷は同容量の2D図面の100倍を超える。
バックグラウンドバッチ処理の信頼性を担保するためには、以下のメトリクスを事前に取得する必要がある。
- ModelSpaceのエンティティ数: 幾何学計算の負荷に直結する。
- ブロック定義(BlockTableRecord)の数: 参照(INSERT)のネスト深度と合わせ、シンボル展開の負荷を予測する。
- 画層・線種・文字スタイルの定義数: テーブル情報の肥大化による名前解決のコスト。
これらを `Documents.Open` なしで実現するためには、オブジェクトDBの非アタッチ読込(Database Objectの単独生成)、あるいはType Libraryの限界を突くアプローチが必要となる。
—
2. 実装コード:開かずにエンティティ数を暴く
以下のVBAコードは、AutoCADのプロセスを完全に表に出さず(あるいは図面ウィンドウを開かずに)、指定したDWGファイルのデータベース構造へ安全にアクセスし、オブジェクト総数を極低負荷で算出する実務モジュールである。
Option Explicit
‘ ==============================================================================
‘ モジュール名: modDrawingLoadPredictor
‘ 概要: 図面を開かずにDWG内のオブジェクト数をカウントし、処理負荷を予測する
‘ 依存関係: AutoCAD Type Library
‘ ==============================================================================
Public Type DrawingMetrics
Filepath As String
TotalEntities As Long
ModelSpaceCount As Long
BlockCount As Long
LayerCount As Long
EstimatedLoadLevel As String
IsReadable As Boolean
End Type
‘ 処理負荷のしきい値定義(実務チューニング用)
Private Const THRESHOLD_HEAVY As Long = 50000
Private Const THRESHOLD_MODERATE As Long = 10000
Public Function PredictDrawingLoad(ByVal fullPath As String) As DrawingMetrics
Dim metrics As DrawingMetrics
metrics.Filepath = fullPath
metrics.IsReadable = False
‘ ファイル存在確認
If Dir(fullPath) = “” Then
metrics.EstimatedLoadLevel = “ERROR: File Not Found”
PredictDrawingLoad = metrics
Exit Function
End If
Dim targetDb As AcadDatabase
On Error GoTo ErrorHandler
‘ 【極限の知見】
‘ 画面描画(Documents.Add / Open)を行わず、新規作成した無名DBの裏で
‘ ターゲットDWGのオブジェクトデータベースを読込専用(True)でアタッチする。
‘ これにより、UIスレッドを完全にバイパスし、メモリ消費を最小限に抑える。
Set targetDb = ThisDrawing.Application.Workdatabases.Add(fullPath)
Dim msCount As Long
Dim blkCount As Long
Dim layCount As Long
Dim ent As AcadEntity
Dim block As AcadBlock
‘ 1. モデル空間のエンティティ数をカウント
msCount = targetDb.ModelSpace.Count
‘ 2. ペーパー空間・ブロック定義内のエンティティを再帰的または網羅的にカウント
blkCount = 0
For Each block In targetDb.Blocks
‘ 外部参照やレイアウト固有のシステムブロックを除外して純粋なブロックをカウントする場合のフィルタ
If Not (block.IsLayout) Then
blkCount = blkCount + block.Count
End If
Next block
‘ 3. 画層テーブルの肥大度チェック
layCount = targetDb.Layers.Count
‘ メトリクスの格納
metrics.ModelSpaceCount = msCount
metrics.BlockCount = blkCount
metrics.LayerCount = layCount
metrics.TotalEntities = msCount + blkCount
metrics.IsReadable = True
‘ 4. 負荷レベルの判定
If metrics.TotalEntities >= THRESHOLD_HEAVY Then
metrics.EstimatedLoadLevel = “HEAVY (要バッチ分割・メモリ解放制御)”
ElseIf metrics.TotalEntities >= THRESHOLD_MODERATE Then
metrics.EstimatedLoadLevel = “MODERATE (標準処理)”
Else
metrics.EstimatedLoadLevel = “LIGHT (高速処理可能)”
End If
CleanUp:
‘ 【重要】COMオブジェクトの明示的解放とWorkdatabaseの切断
‘ これを怠ると、大量ループ時にAutoCADのプロセス空間がメモリリークで破綻する。
If Not targetDb Is Nothing Then
‘ Workdatabasesコレクションから安全にアンロード
ThisDrawing.Application.Workdatabases.Close targetDb
End If
Set targetDb = Nothing
PredictDrawingLoad = metrics
Exit Function
ErrorHandler:
metrics.EstimatedLoadLevel = “ERROR: ” & Err.Description
metrics.IsReadable = False
Resume CleanUp
End Function
‘ 実行テスト用プロシージャ
Public Sub Sample_BatchPrediction()
Dim targetFile As String
targetFile = “C:\CAD_Data\LargeAssembly_2023.dwg”
Dim res As DrawingMetrics
res = PredictDrawingLoad(targetFile)
If res.IsReadable Then
Debug.Print “=== 予測診断結果 ===”
Debug.Print “対象ファイル: ” & res.Filepath
Debug.Print “ModelSpace数: ” & res.ModelSpaceCount
Debug.Print “Block内要素数: ” & res.BlockCount
Debug.Print “総オブジェクト: ” & res.TotalEntities
Debug.Print “画層数 : ” & res.LayerCount
Debug.Print “判定負荷レベル: ” & res.EstimatedLoadLevel
Else
Debug.Print “解析失敗: ” & res.EstimatedLoadLevel
End If
End Sub
—
3. コードの急所:なぜ `Workdatabases` なのか?
通常のVBA開発者は、図面を開こうとするときに `Documents.Open(Path)` を選びがちだ。しかし、`Documents` コレクションを操作することは、AutoCADのMDI(マルチドキュメントインターフェイス)環境に直接干渉し、強制的にドキュメントウィンドウをアクティブ化する。
これに対し、ここで使用している `Workdatabases` は、画面上に一切の視覚的表現(UI)を持たず、メモリ上に純粋なCADデータ構造(AcDbDatabase)だけを構築するためのAPIである。
このアプローチを取ることで、以下の圧倒的なアドバンテージを得られる。
1. 描画プロセスの完全排除: `Regen` や `Zoom Extents` が走る余地がないため、CPU使用率が激減する。
2. トランザクション的安全性: 読込専用でアタッチするため、誤って元図面を上書き保存するリスクが構造的にゼロになる。
3. 並列処理(擬似)の布石: UIロックが発生しないため、将来的にCOMオートメーションを利用した複数プロセスの制御において極めて有利に働く。
—
4. レガシー環境・大規模バッチにおける保守性とメモリ管理の鉄則
AutoCAD VBAにおいて最大の敵は、「COMオブジェクトの解放漏れによるメモリ肥大化」である。特に `Workdatabases.Add` や `Blocks` コレクションのイテレーションを数千回繰り返すバッチ処理では、わずかな参照の残存が致命傷となる。
チーフアーキテクトからの厳格な実装指針:
- 変数のスコープを最小限に絞る: オブジェクト変数(`AcadEntity`, `AcadBlock` 等)はループ内で宣言せず、ループ外で定義するか、明示的に `Set xxx = Nothing` をループの各イテレーションの末尾、あるいはプロシージャ脱出時に必ず実行すること。
- エラーハンドリングの強制: `On Error GoTo` を実装しないコードは、バッチ処理において「時限爆弾」と同義である。例外発生時にも必ず `Workdatabases.Close` がコールされる構造を担保しろ。
- バージョン互換の罠: 近年のAutoCAD形式(2018形式以降など)のDWGを、旧型のAutoCADエンジンからWorkdatabase経由で読み込もうとした場合、内部コンバータが例外を吐くことがある。ファイルバージョンの事前チェック(バイナリヘッダの先頭6バイト読込など)と組み合わせることで、システム全体の堅牢性は極限まで高まる。
—
総括
図面を開かずにリソースを予測する技術は、単なる「効率化」ではない。それは、システム全体の予測可能性を高め、夜間バッチや自動化サーバーの信頼性を担保するためのエンジニアリングの基本原則である。
「動くからいいや」という甘えを捨て、オブジェクトのライフサイクルとメモリの挙動を完全に支配せよ。それこそが、AutoCAD VBAを真に掌握する者の姿である。
