【上級】AutoCAD VBAでのマルチドキュメント制御:パスワード保護と外部参照の完全自動化
AutoCAD VBAを用いたバッチ処理や大規模な図面マイグレーションにおいて、最大の敵は「ダイアログによる処理の停止」である。
特に、数千枚単位の図面群をループ処理している最中に、パスワード保護されたファイルや、外部参照(Xref)のパスが見つからない図面に遭遇した瞬間、VBAの実行スレッドはモーダルダイアログの裏側で完全に凍結する。開発者が席を外している夜間にこれが起これば、翌朝に出社したとき画面には無慈悲に点滅するダイアログと、1枚目で停止したままのタスクマネージャーが残されることになる。
本稿では、`AcadApplication` および `Documents` コレクションの限界を突破し、COMの裏側でうごめくAutoCADのライフサイクルを完全に掌握するための実践的知見を、シニアエンジニアの視点から解説する。
—
1. AutoCADマルチドキュメント環境の構造的理解
AutoCADのVBA環境(VBA IDE)は、原則として `AcadApplication`(単一のプロセス)の配下で動作する。しかし、AutoCAD 2000以降、SDI(シングルドキュメントインターフェース)からMDI(マルチドキュメントインターフェース)へとアーキテクチャが移行したことにより、メモリ空間内には複数の `AcadDocument` が存在し得る。
ここで重要なのは、VBAから開くドキュメントと、ユーザーがGUI操作で開くドキュメントでは、イベントの捕捉方法やエラーの伝播経路が異なるという点だ。
通常、ドキュメントを開くには以下のメソッドを使用する:
Dim oDoc As AcadDocument
Set oDoc = ThisDrawing.Application.Documents.Open(“C:\Path\To\Drawing.dwg”)
しかし、この標準的な `.Open` メソッドは、対象図面がパスワード保護されている場合や、外部参照の解決を求めるダイアログが発生した場合、容赦なくコードの実行を中断し、ユーザーの介入を待機する。これをコード側から完全に制御するには、AutoCADのシステム変数と、COMオブジェクトのライフサイクル管理を同期させる必要がある。
—
2. パスワード保護された図面の自動ハンドリング
AutoCADのファイルオープン時にパスワードを渡すオーバーロードは、残念ながら標準のVBA `Documents.Open` には用意されていない(COMインターフェースの制限)。パスワード付き図面を自動処理するためには、「ファイルオープン時のイベントフック」 または 「SendKeys / UI Automationによる介入」、あるいは 「ObjectDBXの活用」 を検討する必要がある。
しかし、ObjectDBX(`ObjectDBX.AxDbDocument`)は高速である一方で、一部のエンティティや外部参照の解決において制限がある。真にフル機能のAutoCADドキュメントとして開く必要がある場合、システム変数とエラーハンドリングを組み合わせた堅牢な実装が求められる。
以下は、パスワード保護や読み込みエラーをトラップし、バッチ処理を止めずにスキップ・ログ出力するための高度なエラーハンドリング実装である。
Option Explicit
Public Sub BatchProcessDrawings(ByVal targetFolderPath As String)
Dim fso As Object
Dim fldr As Object
Dim file As Object
Dim oApp As AcadApplication
Dim oDoc As AcadDocument
Set fso = CreateObject(“Scripting.FileSystemObject”)
Set fldr = fso.GetFolder(targetFolderPath)
Set oApp = ThisDrawing.Application
アラート抑制 oApp, True ‘ システム変数の最適化
On Error GoTo BatchError
For Each file In fldr.Files
If LCase(fso.GetExtensionName(file.Name)) = “dwg” Then
‘ 個別ファイルの処理におけるエラーを独立させるためにトラップを分ける
On Error GoTo FileOpenError
‘ ドキュメントを開く(読み取り専用、または通常)
Set oDoc = oApp.Documents.Open(file.Path, False)
‘ — ここに図面に対する処理を記述 —
Call ProcessDrawingInternal(oDoc)
‘ 変更を保存せずに閉じる(必要に応じてSave)
oDoc.Close False
Set oDoc = Nothing
On Error GoTo BatchError
End If
Next file
CleanUp:
アラート抑制 oApp, False
Exit Sub
FileOpenError:
‘ パスワード保護、あるいはファイル破損等で開けなかった場合のエラー処理
Debug.Print “[SKIP] 処理不能ファイル: ” & file.Path & ” (Error: ” & Err.Description & “)”
‘ 念のためオブジェクトの参照を解放
If Not oDoc Is Nothing Then
On Error Resume Next
oDoc.Close False
Set oDoc = Nothing
On Error GoTo BatchError
End If
Resume Next ‘ 次のファイルへ強制継続
BatchError:
MsgBox “致命的なエラーが発生しました: ” & Err.Description, vbCritical
Resume CleanUp
End Sub
Private Sub アラート抑制(ByVal app As AcadApplication, ByVal suppress As Boolean)
If suppress Then
app.SetSystemVariable “FILEDIA”, 0 ‘ ダイアログを非表示にしコマンドライン入力に強制
app.SetSystemVariable “CMDDIA”, 0
app.SetSystemVariable “REGENMODE”, 0 ‘ 再描画を抑制してパフォーマンスを極限まで引き上げる
Else
app.SetSystemVariable “FILEDIA”, 1
app.SetSystemVariable “CMDDIA”, 1
app.SetSystemVariable “REGENMODE”, 1
End If
End Sub
Private Sub ProcessDrawingInternal(ByRef doc As AcadDocument)
‘ 実際のエンティティ操作をここに記述
‘ 例: 全レイ法のロック解除など
Dim lyr As AcadLayer
For Each lyr In doc.Layers
lyr.Lock = False
Next lyr
End Sub
—
3. 外部参照(Xref)の未解決・欠落ダイアログの完全封殺
外部参照がリンクされた図面を `Documents.Open` で開く際、参照先のファイルパスが見つからない場合、AutoCADはデフォルトで「外部参照ファイルをが見つかりません」というモーダルダイアログを表示する。
これを防ぐためには、単に `FILEDIA = 0` にするだけでは不十分な場合がある。AutoCADのコアエンジンは、Xrefのロード時にファイルパスの解決を試み、失敗するとホストアプリケーションのメッセージループに割り込んでダイアログを描画しようとする。
これに対処するため、事前にシステム変数 `XLOADCTL`(外部参照ロード制御) および `DEMANDLOAD` を制御し、さらに `OBJECTDBX` をファーストチョイスとして検討するというアーキテクチャ上の判断が必要になる。
ObjectDBXを用いた外部参照非依存の高速処理
もし図面のジオメトリデータ(線分、文字など)の解析や一括置換が目的であり、外部参照のライブな解決が不要な場合、`Documents.Open` を使ってはならない。メモリを無駄に消費し、UIのオーバーヘッドとダイアログのリスクを背負うことになる。
代わりに、`ObjectDBX` を用いたバックグラウンド処理を実装すべきである。
Public Sub ProcessWithObjectDBX(ByVal drawingPath As String)
Dim dbxDoc As Object
‘ AutoCADのバージョンに応じたObjectDBXのProgIDを取得
‘ AutoCAD 2024の場合: “ObjectDBX.AxDbDocument.24” (バージョンにより適宜変更)
Set dbxDoc = CreateObject(“ObjectDBX.AxDbDocument.16”) ‘ AutoCAD 2004-2006基準例
On Error GoTo DbxError
‘ GUIを立ち上げずにファイルをサイレントオープン
dbxDoc.Open drawingPath
‘ 外部参照や重たいイベントを引き起こさずにデータ構造へアクセス
Dim ent As AcadEntity
For Each ent In dbxDoc.Database.ModelSpace
‘ エンティティ操作
Next ent
‘ 変更を保存して閉じる
dbxDoc.SaveAs drawingPath
Set dbxDoc = Nothing
Exit Sub
DbxError:
Debug.Print “[DBX ERROR] ” & drawingPath & ” : ” & Err.Description
Set dbxDoc = Nothing
End Sub
※注意: ObjectDBXは画面(AcadApplication.Documents)にドキュメントを追加しないため、純粋なデータ処理においてはパフォーマンスが桁違いに高い。ただし、一部のUI依存メソッドや特定のアドオンオブジェクトが絡む図面では開けないジレンマがあるため、対象図面の性質によって `Documents.Open` と使い分ける必要がある。
—
4. COMオブジェクトのメモリリークを防ぐ厳格なライフサイクル管理
AutoCAD VBAで最も見落とされるのが、COMラッパーの参照カウントとメモリリークである。
VBAのガベージコレクションは信頼性が低く、特に `For Each` ループ内で `AcadDocument` や `AcadEntity` を取得し、明示的に `Set xxx = Nothing` を行わなかった場合、AutoCADのプロセス(acad.exe)内にCOMの参照が残り続け、数百枚の処理を行った段階でメモリ不足(Out of Memory)やフリーズを引き起こす。
鉄則:参照の即時解放とエラー時の確実なクリーンアップ
1. ループ変数や一時オブジェクトは、使い終わったら即座に `Set obj = Nothing` を行う。
2. エラー発生時(`On Error GoTo`)でも確実にオブジェクトが破棄されるよう、クリーンアップブロックを必ず通す設計にする。
3. `ThisDrawing` への過度な依存を避け、明示的にインスタンス化したアプリケーション・ドキュメント参照を使用する。
—
5. チーフアーキテクトからの提言:さらなる高みへ
本稿で解説した手法(システム変数によるUIの完全抑制、エラーハンドリングによるフォールトトレランス、ObjectDBXの選択的活用)を導入すれば、深夜のバッチ処理がダイアログで停止するという悪夢から解放される。
しかし、真にモダンでスケーラブルな自動化を目指すのであれば、VBAというレガシーなランタイムの制約から脱却し、.NET API (C# / VB.NET) による外手からのCOM制御(あるいはManaged AutoCAD API)への移行を視野に入れるべきだ。C#であれば、`DocumentCollection.Open` のオーバーロードや、より洗練された例外処理、さらにはマルチスレッド処理(特定制限あり)の恩恵を受けることができる。
レガシーなVBA環境を維持せざるを得ない現場であっても、ここで紹介したアーキテクチャ的思考を取り入れることで、コードの堅牢性は劇的に向上する。手元のスクリプトを「おもちゃ」から「エンタープライズグレードの自動化エンジン」へと昇華させてほしい。
