AutoCAD VBAを掌握する極限の知見:複数バージョン共存環境における「COM参照の動的解決」とオープン制御
現場を見渡せば、AutoCAD 2022、2023、2024、そして最新の2025が混在するカオスな環境が日常となっている。
異なるバージョンのCADが同居するマシン上で、社内ニッチな自動化VBAを走らせたとき、「ActiveXコンポーネントはオブジェクトを作成できません(エラー 429)」や、意図しないバージョンのバックグラウンドプロセスが暴走する現象に直面したシニアエンジニアは少なくないはずだ。
静的バインド(早期バインド)の甘い幻想を捨て、WindowsのCOMアーキテクチャの根幹と動的バインド(遅延バインド)、そしてレジストリ制御を理解した者だけが、このバージョン共存の地獄を制圧できる。
今回は、複数バージョン共存環境下で「どのバージョンのAutoCADを掴み、どう安全にDWGを開き、そして如何に確実にメモリを解放するか」の極限の知見を公開する。
—
1. 静的バインドの呪縛と「動的解決(Late Binding)」への移行
VBAの「参照設定」から `AutoCAD x.x Type Library` をチェックする開発スタイルは、開発初期こそインテリセンスの恩恵を受けられるが、複数バージョン共存環境では「毒」でしかない。
参照設定を行った(=静的バインド)コードは、コンパイル時に特定のCLSID(クラス識別子)に固定される。例えば、AutoCAD 2023の参照設定をしたマクロを、AutoCAD 2024しか入っていない環境、あるいは両方入っていてもデフォルトのProgIDが競合する環境で実行すると、容赦なくエラーが返る。
解決策:ProgIDの動的生成と `CreateObject`
特定のバージョンを狙い撃ちするか、あるいはインストールされている最新バージョンを動的に特定してインスタンスを生成するには、`CreateObject` または `GetObject` を用いた動的バインド(遅延バインド)を徹底する。
‘ ==============================================================================
指定したバージョンのAutoCADインスタンスを安全に取得・起動する関数
‘ ==============================================================================
Public Function GetAutoCADApplication(Optional ByVal targetVersion As String = “”) As Object
Dim progID As String
Dim acadApp As Object
On Error GoTo ErrorHandler
‘ バージョン指定がない場合は、実行環境で利用可能な最新(またはデフォルト)を狙う
If targetVersion = “” Then
progID = “AutoCAD.Application”
Else
‘ 例: “AutoCAD.Application.24” (2023), “AutoCAD.Application.25” (2024) 等
progID = “AutoCAD.Application.” & targetVersion
End If
‘ 既存のインスタンスにアタッチするか、新規起動を試みる
On Error Resume Next
Set acadApp = GetObject(, progID)
If acadApp Is Nothing Then
Set acadApp = CreateObject(progID)
End If
On Error GoTo ErrorHandler
If acadApp Is Nothing Then
Err.Raise vbObjectError + 1000, “GetAutoCADApplication”, “AutoCADのインスタンス生成に失敗しました。バージョン: ” & targetVersion
End If
Set GetAutoCADApplication = acadApp
Exit Function
ErrorHandler:
MsgBox “AutoCADセッションの確立に失敗しました。” & vbCrLf & _
“エラー番号: ” & Err.Number & vbCrLf & _
“詳細: ” & Err.Description, vbCritical, “COM動的解決エラー”
Set GetAutoCADApplication = Nothing
End Function
—
2. 複数バージョン共存時の「アクティブ化競合」とCOMの罠
複数バージョンがインストールされている場合、シェル(OS)が管理するファイル関連付けや、最後にインストールされたAutoCADのCOMサーバー情報(レジストリの `HKEY_CLASSES_ROOT\AutoCAD.Application`)が意図せず書き換わっていることがある。
「`CreateObject(“AutoCAD.Application”)`」をコールした際、どのバージョンの `acad.exe` がバックグラウンドで起動するかは、レジストリの `CurVer` キー、またはCOMのマーシャリングに依存する。確実な制御を行うには、明示的にバージョンサフィックス(例: `.24` など)を指定するか、後述するレジストリ直接参照によるバージョン列挙アプローチが必要になる。
インストール済みバージョンのレジストリ動的スキャン
もしクライアント環境ごとにどのバージョンが入っているか分からない場合、VBAからWMIやRegistryを叩いて利用可能なAutoCADのProgIDを動的に逆引きする設計が、プロフェッショナルには求められる。
‘ レジストリからインストール済みのAutoCADバージョンを列挙する概念コード(抜粋)
‘ HKEY_CLASSES_ROOT\AutoCAD.Application. を走査し、利用可能な最新バージョンを決定する
実務上は、起動時にユーザーへバージョンを選択させるか、iniファイル/環境変数で対象バージョンを外出しして `GetAutoCADApplication(“24”)` のようにピンポイントでバインドするのが最も堅牢である。
—
3. オープン制御:Read-Only排他制御とバージョン互換の罠
AutoCAD VBAで最も恐ろしいのは、「図面を開いたままプロセスが死に、ロックファイル(`.dwl` / `.dwl2`)が残る現象」、そして「上位バージョンで保存された図面を下位バージョンで強制オープンしようとした際のサイレントクラッシュ」だ。
堅牢な `Documents.Open` 実装
以下のコードは、指定したDWGファイルを指定バージョンのAutoCAD上で安全に開き、読み取り専用(Read-Only)モードでの排他制御、およびバージョンミスマッチによるフリーズを防ぐための実践的なオープン制御である。
‘ ==============================================================================
‘ 堅牢なDWGファイルオープンプロシージャ
‘ ==============================================================================
Public Sub SafeOpenDrawing(ByVal acadApp As Object, ByVal filePath As String, Optional ByVal openReadOnly As Boolean = True)
Dim docCol As Object
Dim targetDoc As Object
Dim fso As Object
Set fso = CreateObject(“Scripting.FileSystemObject”)
‘ ファイル存在確認
If Not fso.FileExists(filePath) Then
MsgBox “指定された図面ファイルが存在しません。” & vbCrLf & filePath, vbExclamation, “ファイルエラー”
Exit Sub
End If
‘ ドキュメントコレクションの取得
Set docCol = acadApp.Documents
On Error GoTo OpenError
‘ 可視化の保証(バックグラウンド実行であっても、COM安定性のために一度Visibleを推奨する場合がある)
acadApp.Visible = True
‘ すでに同じファイルが開かれているかチェック
Dim doc As Object
For Each doc In docCol
If StrComp(doc.FullName, filePath, vbTextCompare) = 0 Then
Set targetDoc = doc
Exit For
End If
Next doc
‘ 開かれていない場合は新規にOpen
If targetDoc Is Nothing Then
‘ Documents.Open(FullName, [ReadOnly], [Password])
Set targetDoc = docCol.Open(filePath, openReadOnly)
End If
‘ アクティブウインドウの設定
acadApp.ActiveDocument = targetDoc
MsgBox “図面のオープンに成功しました: ” & targetDoc.Name, vbInformation, “成功”
‘ オブジェクト変数のクリーンアップ
Set targetDoc = Nothing
Set docCol = Nothing
Set fso = Nothing
Exit Sub
OpenError:
MsgBox “図面のオープン中に致命的なエラーが発生しました。” & vbCrLf & _
“エラー番号: ” & Err.Number & vbCrLf & _
“説明: ” & Err.Description, vbCritical, “AutoCAD COM Error”
Set targetDoc = Nothing
Set docCol = Nothing
Set fso = Nothing
End Sub
—
4. ライフサイクル管理とメモリ最適化:COMリークの完全撲滅
AutoCAD VBA開発において、初心者が最も見落としがちで、かつ致命的なのが「COMオブジェクトの解放漏れによるメモリリークとゾンビプロセスの残留」である。
VBAのガベージコレクションは頼りにならない。特に動的バインド(`CreateObject` / `GetObject`)を使用した場合、参照カウントが適切にデクリメントされないと、VBAマクロが終了した後も `acad.exe` のプロセスがタスクマネージャーに残り続け、メモリを食い潰し、次回の実行時にファイルロックやCOMクラッシュを引き起こす。
鉄則:オブジェクト変数の明示的 `Nothing` 代入とエラーハンドリング
‘ ==============================================================================
‘ 完全なクリーンアップを行うメイン処理のテンプレート
‘ ==============================================================================
Public Sub MasterAutomationProcess()
Dim acadApp As Object
Dim acadDoc As Object
Dim mSpace As Object
On Error GoTo ErrorHandler
キン
‘ 1. 動的バインドによるインスタンス取得
Set acadApp = GetAutoCADApplication(“24”) ‘ 例: AutoCAD 2023/2024
If acadApp Is Nothing Then Exit Sub
‘ 2. 処理の実行
Set acadDoc = acadApp.ActiveDocument
If Not acadDoc Is Nothing Then
Set mSpace = acadDoc.ModelSpace
‘ — ここに実際の図形操作・解析コードを記述 —
Debug.Print “現在のアクティブ図面: ” & acadDoc.Name
Debug.Print “モデルスペースのエンティティ数: ” & mSpace.Count
‘ ———————————————
End If
CleanUp:
‘ 3. 逆順での厳密なオブジェクト解放(極めて重要)
On Error Resume Next
If Not mSpace Is Nothing Then Set mSpace = Nothing
If Not acadDoc Is Nothing Then Set acadDoc = Nothing
‘ 注意: acadApp.Quit を呼ぶべきかは要件による。
‘ ユーザーがすでに立ち上げているAutoCADをアタッチした場合は Quit を呼んではならない。
‘ ここでは自前で新規起動した場合のみ終了する制御を入れるのがプロの技。
‘ if bCreatedNewInstance then acadApp.Quit
If Not acadApp Is Nothing Then Set acadApp = Nothing
On Error GoTo 0
Exit Sub
ErrorHandler:
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical
Resume CleanUp
End Sub
プロセス残留を防ぐためのアーキテクチャ上の注意点
1. `GetObject` と `CreateObject` の使い分け: ユーザーが手動で立ち上げているAutoCADセッションを勝手に `Quit` させてはならない。起動状態をフラグで管理し、自動起動した場合のみクリーンアップ時にセッションを閉じる、あるいはバックグラウンド(`Visible = False`)で動作させたインスタンスは確実に破棄する設計にする。
2. ループ内での変数生成の禁止: ループ内で `Set ent = mSpace.Item(i)` のような操作を行う場合、VBAの内部バッファやCOMラッパーが肥大化することがある。大量のエンティティを走査する場合は、配列(`GetRows` 相当の概念はないが `Variant` 配列への一括取り出しや、適切な変数再利用)を意識する。
—
終わりに:レガシーとモダンを繋ぐエンジニアリング
AutoCAD VBAは、時代遅れのテクノロジーと揶揄されることもある。しかし、製造業や建設業の現場において、数千枚の図面を夜間に一括処理し、規格検証からレイヤー整理までを完遂する自動化基盤として、今なおその価値は揺るぎない。
複数バージョンの共存というモダンなデスクトップ環境の課題に対し、COMの内部挙動を理解した動的バインドと、ミリ単位のメモリ管理(ライフサイクル制御)を適用することで、VBAは「不安定なオモチャ」から「工業レベルの堅牢な基幹システム」へと昇華する。
コードの1行、`Set` と `Nothing` の1対の裏側に、システムアーキテクトとしての全技術を叩き込め。
