AutoCAD VBAを掌握する極限の知見:非表示画層オブジェクトの一括抽出・集計術
実務において、AutoCADの図面データ構造をVBAから完全に支配することは、単なるメソッドの呼び出し以上の知見を要求される。特に「画面上に見えていない情報」を正確に捕捉する処理は、設計データの監査、数量拾い、他システムへのデータ移行において極めて重要である。
本稿では、`Layers`コレクションの `LayerOn` プロパティ(および凍結状態)を厳密に評価し、非表示画層に潜むオブジェクトのみを高速かつ安全に抽出・集計する、実務中級者からシニア向けのアーキテクチャを解説する。
—
1. AutoCADオブジェクトモデルの深層:なぜ「非表示」の判定でミスが起きるのか
AutoCADの画層(Layer)管理において、オブジェクトが「見えていない」状態には複数の要因が絡み合っている。多くの初心者は単に `LayerOn = False` だけをチェックして痛い目をみる。
実務上、オブジェクトが非表示になる要因は以下の3点に大別される。
1. 画層のOFF (`LayerOn = False`): 画面表示から除外されているが、メモリ上には存在し、選択セットの対象になり得る。
2. 画層の凍結 (`Freeze = True`): ビューポートごとの制御やグローバル凍結により、データベースレベルで実質的に無視される状態。
3. オブジェクト自体の可視性 (`Visible = False`): 画層とは無関係に、エンティティレベルで非表示にされているケース。
数量拾い漏れを防ぐためには、これらを複合的に判定し、さらに `ModelSpace` や `PaperSpace` の走査において、不要なCOMオブジェクトの生成とメモリリークを徹底的に排除しなければならない。
—
2. 実装アーキテクチャ:高速抽出・集計エンジン
以下のコードは、レガシーなAutoCAD環境から最新のセッションまで安定して動作し、COMの参照リーク(メモリ肥大化)を防ぐためにオブジェクト変数を確実に解放(`Nothing`代入)する極限まで最適化されたVBAモジュールである。
Option Explicit
‘ =====================================================================================
‘ 処理名: 非表示画層オブジェクト一括集計エンジン
‘ 概要: ActiveDocument内の全画層の状態を走査し、非表示画層に属するエンティティを
‘ 効率的に抽出してエンティティタイプ別に集計する。
‘ =====================================================================================
Public Sub AuditHiddenLayerEntities()
Dim startTime As Double
startTime = Timer
Dim acadApp As AcadApplication
Dim acadDoc As AcadDocument
‘ エラーハンドリングの要:COM接続の確実な捕捉
On Error GoTo ErrorHandler
Set acadApp = ThisDrawing.Application ‘ またはGetObject(, “AutoCAD.Application”)
Set acadDoc = acadApp.ActiveDocument
‘ 画面更新の抑止(COMパフォーマンスチューニングの基本)
acadApp.ZoomExtents
acadDoc.Utility.Prompt vbCrLf & “>>> 非表示画層の走査を開始します…” & vbCrLf
‘ 1. 画層の状態をキャッシュ(高速化のため毎回APIを叩かない)
‘ キー: 画層名 (String), 値: 非表示か否か (Boolean)
Dim hiddenLayers As Object
Set hiddenLayers = CreateObject(“Scripting.Dictionary”)
Dim lyr As AcadLayer
For Each lyr In acadDoc.Layers
‘ LayerOnがFalse、またはFreezeがTrueのものは「非表示画層」とみなす
If (lyr.LayerOn = False) Or (lyr.Freeze = True) Then
If Not hiddenLayers.Exists(lyr.Name) Then
hiddenLayers.Add lyr.Name, True
End If
End If
Next lyr
If hiddenLayers.Count = 0 Then
MsgBox “非表示状態の画層は検出されませんでした。”, vbInformation, “監査完了”
GoTo CleanUp
End If
‘ 2. エンティティの集計用Dictionary
‘ キー: EntityName, 値: 数量 (Long)
Dim summaryDict As Object
Set summaryDict = CreateObject(“Scripting.Dictionary”)
‘ 3. ModelSpaceの走査(PaperSpaceも対象にする場合は拡張可能)
Dim ent As AcadEntity
Dim targetCount As Long
targetCount = 0
Dim mSpace As AcadModelSpace
Set mSpace = acadDoc.ModelSpace
For Each ent In mSpace
‘ オブジェクト自体の可視性、および所属画層が非表示リストにあるかを判定
If ent.Visible Then
If hiddenLayers.Exists(ent.Layer) Then
Dim eName As String
eName = ent.EntityName
If summaryDict.Exists(eName) Then
summaryDict(eName) = summaryDict(eName) + 1
Else
summaryDict.Add eName, 1
End If
targetCount = targetCount + 1
End If
End If
‘ ループ内でのCOM参照解放はVBAでは変数上書きで自然解放されるが、
‘ 大規模図面ではメモリプレッシャーに注意
Next ent
‘ 4. 結果の出力(イミディエイトウィンドウおよびレポート生成)
Call OutputReport(hiddenLayers.Count, targetCount, summaryDict, Timer – startTime)
CleanUp:
‘ 厳格なメモリ解放(COMオブジェクトの参照カウントをデクリメント)
Set ent = Nothing
Set mSpace = Nothing
Set lyr = Nothing
Set hiddenLayers = Nothing
Set summaryDict = Nothing
Set acadDoc = Nothing
Set acadApp = Nothing
Exit Sub
ErrorHandler:
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical, “致命的エラー”
Resume CleanUp
End Sub
‘ =====================================================================================
‘ 補助プロシージャ: 集計結果のコンソール出力
‘ =====================================================================================
Private Sub OutputReport(ByVal layerCount As Long, ByVal totalEnts As Long, ByVal dict As Object, ByVal elapTime As Double)
Debug.Print “==================================================”
Debug.Print ” 非表示画層オブジェクト 監査レポート”
Debug.Print “==================================================”
Debug.Print ” 検出された非表示画層数: ” & layerCount
Debug.Print ” 拾い上げた隠しエンティティ総数: ” & totalEnts
Debug.Print ” 処理にかかった時間: ” & Format(elapTime, “0.00秒”)
Debug.Print “————————————————–”
Debug.Print “【内訳】”
Dim key As Variant
For Each key In dict.Keys
Debug.Print ” – ” & key & “: ” & dict(key) & ” 件”
Next key
Debug.Print “==================================================”
MsgBox “非表示画層の集計が完了しました。” & vbCrLf & _
“総エンティティ数: ” & totalEnts & ” 件” & vbCrLf & _
“詳細はイミディエイトウィンドウを確認してください。”, vbInformation, “完了”
End Sub
—
3. チーフアーキテクトが指摘する「実務上の罠」とパフォーマンス最適化
大規模なインフラ図面や建築プラント図面では、数百万のエンティティが存在する。上記のコードをそのまま巨大図面に適用すると、以下の問題に直面する。
① ガベージコレクションとCOMラッパーの肥大化
VBAの `For Each ent In mSpace` ループは、内部でC++のCOMラッパーを次々と生成する。ループが10万回を超えると、VBAのランタイムがメモリ解放を追いつかなくなり、AutoCAD本体ごとフリーズ(あるいはメモリリーク)を起こす。
- 対策: 数十万件規模の図面を扱う場合は、`AcadModelSpace` を直接回すのではなく、SelectionSet(選択セット) を用いてフィルタリングするか、レイヤー名リストをあらかじめSQLライクな条件に落とし込むアプローチが必要となる。
② ブロック参照(`AcadBlockReference`)内部の再帰走査
上記のコードはモデル空間のトップレベルのエンティティしか見ていない。もし非表示画層にある「ブロックのネスト(入れ子構造)」の内部に隠されたオブジェクトまで完璧に拾う必要がある場合、`HasAttributes` や `GetDynamicBlockProperties` を経由して、ブロック定義(`Blocks` コレクション)側を再帰的に走査するアーキテクチャへの拡張が必須となる。
—
4. システム間連携への発展
ここで抽出した非表示画層のオブジェクトデータ(「本当は存在しているのに、図面上で見えないためミスしやすい要素」)は、JSON形式にシリアライズして外部のBIM/CIMサーバーや、ERPの資材管理データベースへREST API経由で送信する前処理として極めて高い価値を持つ。
レガシーなAutoCAD VBAであっても、適切なオブジェクトライフサイクル管理とメモリ最適化を行えば、モダンなエンタープライズシステムの一部として十分に機能させることができる。
「画面に見えているものだけを信じるな。データベースの真実を捉えろ。」——これが、現場を極めるエンジニアの流儀である。
