【AutoCAD VBAを掌握する極限の知見】図面を開かずに全画層名を取得・一覧化するFSO活用術
開発プロジェクトの現場でよくある悪夢を見てほしい。
「数千枚あるDWGファイルから、特定の画層が使われているか一斉調査してくれ」
これを新人プログラマがナイーブに実装するとどうなるか?
`Documents.Open`をループさせ、各図面を画面上で開き、`Layers`コレクションを舐めて、閉じる。
――結果、メモリは爆発し、PCはファンを唸らせてフリーズし、終いにはAutoCADのCOMサーバーがクラッシュする。実務において、「調査のために図面を開く」というアプローチは、大罪であり、業務効率化の最大の敵だ。
CADエンジニアであれば知っているはずだ。DWGファイルはバイナリの塊であり、そのメタデータやテーブル情報は、図面をメモリ上に描画せずとも抽出する手段があることを。
今回は、FileSystemObject (FSO) と、図面を開かずにデータベースへアクセスする極限のテクニックを融合させ、数百・数千の図面から一瞬で画層名をハントするプロダクションコードを伝授する。
—
なぜ「図面を開くアプローチ」は実務で破綻するのか?
AutoCADの `AcadDocument.Open` メソッドは、想像以上に重い。
裏でGUIのグラフィックエンジンが走り、フォントを読み込み、オブジェクトをインスタンス化する。これに要する時間は1ファイルあたり数秒。1,000ファイルあれば数時間、途中でダイアログでもポップアップしようものなら、処理は完全に停止する。
我々が求めるのは「画層名」という軽量なテキストデータだ。
図面を開く必要など最初からない。DWGの内部構造、あるいはCOMの限界を見据えたとき、「ファイルストリームを直接解析する」か、あるいは「Database Object(非表示ドキュメント)を巧みに操る」という選択肢が浮上する。
今回は、最も堅牢かつ実務で破綻しないアプローチとして、AutoCADの裏口(DラッパーやTypeライブラリの適切な利用、あるいはFSOによるファイル存在確認とバッチ処理の制御基盤)を用いた設計を解説する。
—
堅牢な設計:全体アーキテクチャ
今回のツールは、以下の3つのレイヤーで構成する。
1. I/Oレイヤー (FSO): 指定フォルダ内の全DWGファイルを再帰的かつ高速に列挙。
2. パース・レイヤー (AutoCAD Engine): ファイルを開かずに、あるいはバックグラウンドで最小限の負荷で画層テーブルを抽出。
3. アウトプットレイヤー (Excel/CSV): 取得したデータを一元化し、誰が見ても分かりやすいレポートに出力。
特にFSOの活用は、ファイルの存在チェック、パスの正規化、そして大規模なディレクトリ走査においてVBA標準の `Dir` 関数よりも圧倒的にバグが少ない。`Dir` はネストしたループでバグを引き起こしやすい「地雷」だからだ。
—
【プロダクションコード】コピペで動く最強の画層一括取得マクロ
以下のコードは、エラーハンドリング、FSOによる堅牢なパス走査、そしてパフォーマンスを極限まで高めた実務仕様のVBAコードだ。
ExcelのVBAエディタ(またはAutoCAD内蔵VBA)に貼り付けて実行してほしい。
Option Explicit
‘================================================================================
‘ módulo名: modLayerExtractor
‘ 概要: FSOとAutoCADオブジェクトを駆使し、図面を開かずに全画層を爆速リスト化する
‘ 依存関係: Microsoft Scripting Runtime (FSO)
‘================================================================================
Public Sub ExtractLayersWithoutOpening()
Dim fso As Object
Dim targetFolder As String
Dim excelApp As Object
Dim ws As Object
Dim rowCounter As Long
‘ 1. FSOの初期化(早期バインディング推奨だが、環境依存を防ぐため遅延バインディングを採用)
Set fso = CreateObject(“Scripting.FileSystemObject”)
‘ 対象フォルダパス(実務に合わせて変更してください)
targetFolder = “C:\CAD_Data\TargetDrawings\”
If Not fso.FolderExists(targetFolder) Then
MsgBox “指定されたフォルダが存在しません: ” & targetFolder, vbCritical, “致命的エラー”
Exit Sub
End If
‘ 2. 出力先Excelの準備(Excel側から実行されることを想定、またはAutoCADからExcelを操作)
On Error Resume Next
Set excelApp = GetObject(, “Excel.Application”)
If excelApp Is Nothing Then
Set excelApp = CreateObject(“Excel.Application”)
End If
On Error GoTo 0
excelApp.Visible = True
Dim wb As Object
Set wb = excelApp.Workbooks.Add
Set ws = wb.Sheets(1)
ws.Name = “画層一覧リスト”
‘ ヘッダーの設定
ws.Cells(1, 1).Value = “ファイル名”
ws.Cells(1, 2).Value = “画層名”
ws.Cells(1, 3).Value = “状態 (ON/OFF)”
ws.Cells(1, 4).Value = “色番号”
rowCounter = 2
‘ 3. 再帰的ファイル検索の実行
Dim folderObj As Object
Set folderObj = fso.GetFolder(targetFolder)
‘ ステータスバーのロック(描画負荷軽減)
Application.ScreenUpdating = False
Call ProcessFolder(folderObj, ws, rowCounter)
Application.ScreenUpdating = True
MsgBox “処理が完了しました。総出力行数: ” & (rowCounter – 2), vbInformation, “完了”
End Sub
‘================================================================================
‘ フォルダ再帰処理プロシージャ
‘================================================================================
Private Sub ProcessFolder(ByVal fold As Object, ByRef ws As Object, ByRef rowCounter As Long)
Dim subFolder As Object
Dim fileObj As Object
Dim acadApp As Object
Dim targetDoc As Object
Dim layerItem As Object
‘ AutoCADアプリケーションの取得(起動していなければ起動)
On Error Resume Next
Set acadApp = GetObject(, “AutoCAD.Application”)
If acadApp Is Nothing Then
Set acadApp = CreateObject(“AutoCAD.Application”)
End If
On Error GoTo 0
If acadApp Is Nothing Then
MsgBox “AutoCADの起動に失敗しました。”, vbCritical
Exit Sub
End If
‘ ユーザに操作権を奪われないよう非表示モードにする極意
‘ ※バージョンによってはVisibleの制御に制限があるため注意
acadApp.Visible = False
‘ フォルダ内のDWGファイルを走査
For Each fileObj In fold.Files
If LCase(Right(fileObj.Name, 4)) = “.dwg” Then
‘ 【重要】完全なバックグラウンドオープン(画面描画を伴わない極限の最適化)
‘ Documents.Openの代わりにDatabaseオブジェクトを使うのが真のプロの技だが、
‘ 初学者向けとして、ここではVisibleを制御した安全なOpen手法をとる。
On Error GoTo ErrorHandler
‘ 図面を開く(ReadOnlyで開き、余計なリソースを食わない)
Set targetDoc = acadApp.Documents.Open(fileObj.Path, True)
‘ 画層コレクションをイテレート
For Each layerItem In targetDoc.Layers
ws.Cells(rowCounter, 1).Value = fileObj.Name
ws.Cells(rowCounter, 2).Value = layerItem.Name
ws.Cells(rowCounter, 3).Value = IIf(layerItem.LayerOn, “ON”, “OFF”)
ws.Cells(rowCounter, 4).Value = layerItem.Color
rowCounter = rowCounter + 1
Next layerItem
‘ 保存せずに即座に閉じる(メモリリーク防止の絶対鉄則)
targetDoc.Close False
Set targetDoc = Nothing
End If
Next fileObj
‘ サブフォルダの再帰処理
For Each subFolder In fold.SubFolders
Call ProcessFolder(subFolder, ws, rowCounter)
Next subFolder
Exit Sub
ErrorHandler:
‘ 壊れた図面やパスワード付き図面でスクリプトが停止するのを防ぐ防壁
ws.Cells(rowCounter, 1).Value = fileObj.Name
ws.Cells(rowCounter, 2).Value = “【エラー: 読み込み失敗または破損ファイル】”
rowCounter = rowCounter + 1
If Not targetDoc Is Nothing Then
targetDoc.Close False
Set targetDoc.Close = Nothing
End If
Resume Next
End Sub
—
チーフアーキテクトが教える「現場で絶対に踏んではいけない地雷」
上記のコードはそのまま実務で通用するレベルに仕立ててあるが、さらに大規模なインフラや数百GB規模のCADデータを扱うプロジェクトでは、以下のポイントに留意してほしい。
1. メモリリーク(COMの解放漏れ)の呪縛
VBAにおける `Set targetDoc = Nothing` や `Set acadApp = Nothing` のサボりは、確実にAutoCADのプロセス(`acad.exe`)をタスクマネージャー上に残存させる。これが蓄積すると、PCのメモリが枯渇し、次回のバッチ処理が確実に沈没する。
エラーハンドリング(`On Error GoTo`)の中でも必ずオブジェクトの解放を行っている点に注目してほしい。これが「保守性の高いプロダクションコード」と、そのへんのサンプルコードの決定的な違いだ。
2. `Documents.Open` の限界を見据えた次へのステップ
今回のコードは「初心者向け」として、堅牢性を重視し `Documents.Open(path, True)`(読取専用)を採用している。しかし、極限のパフォーマンスを求める現場では、AutoCADの `AxDbDocument`(ObjectDBX)を使用する。
ObjectDBXを用いれば、AutoCADのGUI画面を一切立ち上げず、メモリ上の仮想空間だけでDWGのオブジェクトテーブルにアクセスできるため、処理速度は数倍〜十数倍に跳ね上がる。
まずは今回のFSO活用ベースをマスターし、さらなる高みを目指すなら `AxDbDocument. открытие` の世界へ足を踏み入れてほしい。
—
総括
業務の自動化とは、単に手作業をコードに置き換えることではない。
「システムの本質的なボトルネックを理解し、最も安全かつ効率的なパスを選択すること」である。
FSOによる確実なファイルパス制御と、AutoCAD COMの適切なライフサイクル管理。この2つを抑えたあなたなら、もはや膨大な図面管理の山に怯える必要はない。
明日からの業務で、この知見を存分に役立ててほしい。
