なぜ多くのエンジニアは「図面情報の取得」で処理時間をドブに捨てるのか
現場の図面管理ツールや一括変換マクロのコードレビューをしていると、目を覆いたくなるような実装によく遭遇します。
「指定したフォルダにある100枚のDWGファイルの図面番号と保存先を一覧化したい」
この要件に対して、多くの初級〜中級者が「`Documents.Open`で図面を1枚ずつ開き、`ThisDrawing.Path`を取得しては閉じる」という設計をしてしまいます。DWGファイルの実体は、複雑なデータベースであり、バイナリの塊です。図面をグラフィックエンジンにロードし、メモリ上にオブジェクトを展開し、終わったら破棄する――これを100回繰り返せば、数分から数十分のフリーズが発生します。ユーザーは「AutoCADは重い」と不満を漏らしますが、重いのはAutoCADではなく、アーキテクチャの設計ミスです。
ファイルパスやファイル名、拡張子といった「ファイルシステム上のメタデータ」は、AutoCADのコアエンジンを起動してDWGデータベースを展開しなくても、OS(Windows API/ファイルシステム)のレイヤーでミリ秒単位で取得可能です。
今回は、AutoCAD VBAにおける「現在アクティブな図面の安全なパス取得(`ThisDrawing.Path`)」と、「外部図面を1ミリも開かずに高速列挙する(`Dir`関数 / `FileSystemObject`)」の使い分けを、堅牢なプロダクションコードとともに徹底解説します。
—
1. `ThisDrawing.Path` の真実:スコープと「未保存トラップ」
まず、最も身近な `ThisDrawing.Path` から整理しましょう。これは文字通り、現在AutoCAD上でアクティブになっている図面(`AcadDocument`)が保持するプロパティです。
メリット
- 現在操作中の図面に対してダイレクトにアクセスできる。
- オブジェクトモデルのルートとして、もっとも記述が直感的。
致命的な罠:「未保存図面」のハンドリング
新規作成されたばかりの図面(`Drawing1.dwg` など)は、まだディスク上のセクタに書き込まれていません。この状態で `ThisDrawing.Path` を参照すると何が起きるか。エラーにはならず、「空文字(`””`)」が返ってきます。
この空文字をチェックせずに後続のパス結合処理(文字列連結)に流すと、意図しないカレントディレクトリを破壊したり、ファイル保存処理で実行時エラーを誘発します。
‘ 典型的なダメな例
Dim fullPath As String
fullPath = ThisDrawing.Path & “\” & ThisDrawing.Name ‘ Pathが空だと “\Drawing1.dwg” になる(ルート直下を指す)
—
2. 外部図面を「開かずに」情報を抜き出すアプローチ
ディスク上に眠るDWGファイルの一覧を取得したい場合、AutoCADのAPI(`AcadDocuments`)を触ってはいけません。触るべきはOSのファイルシステムです。
選択肢A:VBA標準の `Dir` 関数
- 利点: 外部ライブラリへの依存がゼロ。Win32 APIの薄いラッパーであるため、実行速度が極めて高速。
- 制約: `Dir`関数は内部で静的なポインタを保持しているため、ネスト(再帰呼び出し)ができない。単一フォルダ内の平坦な走査に特化させる必要があります。
選択肢B:`Scripting.FileSystemObject` (FSO)
- 利点: パス文字列の分解(フォルダ名、ベース名、拡張子)が堅牢に行える。再帰処理も容易。
- 制約: COMオブジェクトの生成オーバーヘッドがわずかにある(とはいえ、DWGを開くコストに比べれば数千分の一)。
実務のベストプラクティスとしては、「高速な列挙には `Dir`、パスの安全な切り分けには自作のパーサーまたは `FileSystemObject` を組み合わせる」のが最強の解となります。
—
3. 実践:コピペで使え、破綻しないプロダクションコード
現場でそのままツールに組み込める、堅牢な2つの標準モジュール用コードを提供します。
コード1:アクティブ図面の安全な情報取得
まずは、未保存図面トラップを完全に封殺したアクティブ図面情報の取得関数です。
Option Explicit
”
‘ @description アクティブ図面の完全パスを取得する(未保存図面の検証付き)
‘ @return {String} 保存済みであれば完全パス。未保存なら空文字を返す。
”
Public Function GetSafeActiveDrawingFullPath() As String
On Error GoTo ErrorHandler
‘ 図面が1枚も開かれていない状態をケア
If Documents.Count = 0 Then
GetSafeActiveDrawingFullPath = vbNullString
Exit Function
End If
‘ ディスク上に一度も保存されていない場合、Pathは空文字になる
If Len(ThisDrawing.Path) = 0 Then
‘ 開発者やユーザーに対して明確な状態を返し、呼び出し元で処理を分岐させる
GetSafeActiveDrawingFullPath = vbNullString
Else
‘ Windowsのパスセパレータを付与して結合
GetSafeActiveDrawingFullPath = ThisDrawing.Path & “\” & ThisDrawing.Name
End If
Exit Function
ErrorHandler:
‘ 予期せぬAutoCADのCOM例外をキャッチ
Debug.Print “GetSafeActiveDrawingFullPath Error: ” & Err.Description
GetSafeActiveDrawingFullPath = vbNullString
End Function
コード2:Dir関数を用いた「図面を開かない」超高速DWG走査エンジン
指定フォルダ直下のDWGファイルを、AutoCADのグラフィックエンジンを一切使わずに列挙し、構造化データ(またはイミディエイトウィンドウ)へ展開するアーキテクチャです。
Option Explicit
”
‘ @description 指定フォルダ内のDWGファイル名と完全パスを図面を開かずに超高速列挙する
‘ @param targetFolderPath {String} 走査対象のフォルダパス
”
Public Sub ListDwgFilesWithoutOpening(ByVal targetFolderPath As String)
Dim fileName As String
Dim fullPath As String
Dim fileCount As Long
Dim startTime As Double
‘ パス末尾のセパレータ(\)の有無を正規化
If Right$(targetFolderPath, 1) <> “\” Then
targetFolderPath = targetFolderPath & “\”
End If
‘ フォルダの存在確認
If Len(Dir(targetFolderPath, vbDirectory)) = 0 Then
MsgBox “指定されたフォルダが存在しません: ” & vbCrLf & targetFolderPath, vbCritical, “エラー”
Exit Sub
End If
‘ パフォーマンス測定開始
startTime = Timer
fileCount = 0
‘ ————————————————————————-
‘ Dir関数の第1引数にフィルタを指定して最初のファイルを取得
‘ vbNormal: 通常ファイルを対象
‘ ————————————————————————-
fileName = Dir(targetFolderPath & “.dwg”, vbNormal)
‘ 高速列挙ループ(AutoCADのメモリ消費ゼロ)
Do While Len(fileName) > 0
‘ 完全パスの構築
fullPath = targetFolderPath & fileName
fileCount = fileCount + 1
‘ ———————————————————————
‘ ここで実際の業務処理(Excelへの出力、DB登録、リストボックスへの追加等)を行う
‘ 図面を開く必要が「まだ」ない点に注目してください
‘ ———————————————————————
Debug.Print “[” & Format$(fileCount, “0000”) & “] ” & fileName
Debug.Print ” Path: ” & fullPath
‘ Dirを引数なしで呼ぶと、同条件の「次のファイル」を取得する
fileName = Dir()
Loop
Dim elapsedTime As Double
elapsedTime = Timer – startTime
Debug.Print “————————————————–”
Debug.Print “スキャン完了: ” & fileCount & ” 件 (” & Format$(elapsedTime, “0.00秒”) & “)”
If fileCount = 0 Then
MsgBox “対象フォルダにDWGファイルは見つかりませんでした。”, vbInformation, “結果”
Else
MsgBox fileCount & ” 件のDWGファイルを高速検出しました。(処理時間: ” & Format$(elapsedTime, “0.00”) & ” 秒)”, _
vbInformation, “完了”
End If
End Sub
”
‘ テスト実行用エントリポイント
”
Public Sub Test_FastDwgScanner()
‘ テスト対象のフォルダ(自身の環境に合わせて書き換えてください)
Dim targetDir As String
targetDir = “C:\Drawings\ProjectA”
Call ListDwgFilesWithoutOpening(targetDir)
End Sub
—
4. プロダクション環境におけるアーキテクトの視点
実務コードを書くにあたり、初心者を脱却するために絶対に抑えておくべきポイントを伝授します。
1. ネットワークパス(UNCパス)の落とし穴
`targetFolderPath` に `\\ServerName\ShareFolder\Project\` のようなUNCパスが指定された場合でも、上記の `Dir` 関数コードは正常に動作します。しかし、ネットワークの切断時やアクセス権限がない場合、`Dir` は実行時エラー(エラー52:ファイル名または番号が不正です)を吐きます。業務アプリでは必ず `On Error` ハンドリングを噛ませてください。
2. 「Dirの再帰呼び出し」は不可
前述の通り、`Dir` の内部ポインタはプロセス内で1つしかありません。そのため、以下のようなコードを書くと無限ループかクラッシュを引き起こします。
‘ 絶対にやってはいけないNGパターン
fileName = Dir(folderPath & “.dwg”)
Do While Len(fileName) > 0
‘ サブフォルダを潜るために再度 Dir を呼ぶと、親ループのポインタが破壊される!
subDir = Dir(folderPath, vbDirectory)
fileName = Dir() ‘ ここで破綻する
Loop
サブフォルダまで潜って再帰的にDWGを検索したい場合は、`Scripting.FileSystemObject` を使用するか、Win32 APIの `FindFirstFile` / `FindNextFile` をクラス化して自作するのが定石です。
3. 大文字小文字の差異を無視する
Windowsファイルシステムは大文字小文字を区別しませんが、文字列比較(`If ext = “.dwg”` など)を行う場合は、VBA側で必ず `LCase$` 関数を通すか、モジュール先頭に `Option Compare Text` を明記してください。`.DWG` や `.Dwg` を見落とすという、実務で極めて恥ずかしいバグを防ぐことができます。
—
まとめ:図面を開くのは「最後の瞬間」だけにせよ
AutoCAD VBAを極めたエンジニアの鉄則は一つです。
> 「図面データベースの中身を書き換える瞬間まで、決して `Documents.Open` を呼ぶな」
ファイル名、更新日時、フォルダ構造の解析、さらにはファイルフォーマットの判別程度であれば、図面を開く必要性は皆無です。
OSのファイルシステム関数(`Dir` や `FSO`)で高速に母集団をフィルタリングし、「本当に処理が必要な図面だけ」を厳選してAutoCADのパイプラインに流し込む。
この設計思想の差が、数時間かかるバッチ処理を数分で終わらせる、本物の業務自動化ツールを生み出す鍵となります。ぜひ、今日からあなたのコードを見直してみてください。
