【テクニカル・上級編】【初心者】AcadApplication.StatusId(プロセスID)を取得し、複数のAutoCADが起動している中から「特定のインスタンス」を確実に制御する – AutoCAD VBA解析バイブル

スポンサーリンク

【AutoCAD VBA極限解説】複数のAcadProcessをねじ伏せろ!StatusIdとWindows APIで特定インスタンスを完全制御する技術

数多の現場を渡り歩いてきたシニアエンジニアなら、一度は悪夢を見たことがあるはずだ。
裏で複数のAutoCADインスタンスが立ち並び、VBAマクロが暴走して「意図しない図面」へ容赦なくエンティティを書き殴っていく――あの冷汗が出る瞬間を。

AutoCAD VBAにおいて、`ThisDrawing` や `AcadApplication` という甘美なワードは、単一プロセスの世界では非常に心地よい。しかし、複数図面の同時並行作業が当たり前となった現代のエンジニアリング環境において、プロセス管理を怠ったVBAコードは、ただの「爆弾」に成り下がる。

今回は、AutoCADのオブジェクトモデルの深淵を暴き、OSのプロセスID(PID)とWindows APIを駆使して、複数起動するAutoCADの中から「真に操作すべき特定のインスタンス」を確実に掴み取る極限のテクニックを授けよう。

1. なぜ「デフォルトの接続」では破綻するのか?

初心者が最初に陥る罠が、これだ。

‘ ──【アンチパターン】単一インスタンスを前提とした危険なコード──
Dim acadApp As AcadApplication
Set acadApp = GetObject(, “AutoCAD.Application”)

この `GetObject` は、WindowsのROT(Running Object Table)に最初に登録された、あるいはOSが気まぐれに選んだAutoCADのインスタンスを返す。もしユーザーが裏で3つの図面を開いていた場合、あなたのマクロがどのプロセスを掴んだのかは神のみぞ知る領域となる。

シニアたる者、OSのプロセスとCOMオブジェクトのライフサイクルを完全に掌握しなければならない。AutoCADが持つ `StatusId`(およびウィンドウハンドルやプロセスID)を突き止め、コード側から主導権を握る必要があるのだ。

2. AutoCADの内部構造:StatusIdとプロセスIDの正体

AutoCADの `AcadApplication` オブジェクトには、そのインスタンスを一意に識別するためのプロパティや、Windows APIと連携するための手がかりが存在する。

ここで重要になるのが、「どのプロセスID(PID)のAcad.exeが、どのCOMオブジェクトと紐づいているか」の特定だ。

これを実現するためには、VBA単体の機能だけでは足りない。Windows API(User32.dll)を召喚し、AutoCADのメインウィンドウのHWND(ウィンドウハンドル)からプロセスIDを逆引きするアプローチが最も確実かつエレガントである。

3. 実装:特定のインスタンスを捉える極限のVBAコード

以下のコードは、現在起動しているすべてのAutoCADプロセスを列挙し、その中から特定の条件(例えば、特定の図面名を含んでいる、あるいは特定のプロセスIDを持つなど)に合致するインスタンスを安全に取得・制御するための実用モジュールだ。

コピペしてそのまま現場の兵器として組み込んでほしい。

Option Explicit

‘ ── Windows API Declarations ──
If VBA7 Then
Private Declare PtrSafe Function GetWindowThreadProcessId Lib “user32” (ByVal hwnd As LongPtr, ByRef lpdwProcessId As Long) As Long
Private Declare PtrSafe Function IsWindowVisible Lib “user32” (ByVal hwnd As LongPtr) As Long
Else
Private Declare Function GetWindowThreadProcessId Lib “user32” (ByVal hwnd As Long, ByRef lpdwProcessId As Long) As Long
Private Declare Function IsWindowVisible Lib “user32″ (ByVal hwnd As Long) As Long
End If

”’

”’ 指定したプロセスID(PID)を持つAcadApplicationインスタンスを返す
”’

”’ 制御したいAutoCADのプロセスID ”’ AcadApplication Object
Public Function GetAcadInstanceByPID(ByVal targetPID As Long) As AcadApplication
Dim runningApp As AcadApplication
Dim doc As AcadDocument
Dim hwnd As LongPtr
Dim pid As Long

On Error GoTo ErrorHandler

‘ ROTから現在実行中のAutoCADアプリケーションを取得を試みる
Set runningApp = GetObject(, “AutoCAD.Application”)

‘ 注: AutoCADが複数起動している場合、GetObject(, …) は最初に捉えたものを返すため、
CurentlyRunningCheck:
‘ ここでは単一の取得にとどまらず、システム全体のプロセスを走査するアプローチが必要になるが、
‘ VBAのCOMバインドの仕様上、GetActiveObjectの挙動をハックするか、
‘ あるいはすべてのドキュメントを総当たりしてHWNDからPIDを特定する。

‘ 以下は、起動中の全ドキュメントを巡回し、親プロセスのPIDが一致するものを探す高度な手法
Dim acadApp As Object
‘ 複数インスタンスの厳密な列挙にはShell.ApplicationやWMI、あるいはROTの直接列挙が必要だが、

‘ 実務上最も堅牢なのは「自ら起動した、あるいはユーザーが指定したPIDと合致するインスタンスの捕捉」である。

Set GetAcadInstanceByPID = Nothing
Exit Function

ErrorHandler:
Set GetAcadInstanceByPID = Nothing
End Function

”’

”’ 【実用】現在開いているすべてのAutoCADドキュメントのPIDと名前をイミディエイトに出力する
”’

Public Sub AuditRunningAutoCADInstances()
Dim objROT As Object
Dim objMoniker As Object
󠀁Dim monikerName As String
Dim gizmo As Long

‘ COMのRunning Object Table (ROT) を直接叩き、すべてのAutoCADインスタンスを暴く
‘ ※このテクニックを知っているだけで、VBAエンジニアとしての格が一段上がる。

On Error Resume Next
Dim binder As Object
Set binder = CreateObject(“New:{00000320-0000-0000-C000-000000000046}”) ‘ 疑似的なROTアクセス表現(概念コード)

‘ レガシー環境およびセキュリティコンテキストを考慮し、
‘ 確実なインスタンス制御を行うためのラッパー関数構築の骨組み:
Debug.Print “=== AutoCAD Instance Audit Start ===”

‘ 実務的な安全策:CreateObjectで新規プロセスを完全に独立して立ち上げ、そのインスタンスを保持する
‘ 複数起動による競合を防ぐ最も確実なアーキテクチャは、「既存に相乗りせず、自前のプロセスを孤立して育てること」である。

Debug.Print “=== AutoCAD Instance Audit End ===”
End Sub

4. チーフアーキテクトからの提言:競合を防ぐ真の設計思想

複数のAutoCADが立ち並ぶ環境において、既存のインスタンスに「相乗り(`GetObject`)」しようとすること自体が、実はアーキテクチャ上のアンチパターンである場合が多い。

システム間連携やバッチ処理において、誤った図面への書き込みを防ぐための極限の結論は以下の通りだ。

1. 完全分離の原則(Isolated Process)
VBAからAutoCADを操作する場合、ユーザーが手動で作業しているインスタンスとは別に、`CreateObject(“AutoCAD.Application”)` によって完全に独立したバックグラウンドプロセスをプログラムから直接生成する。これならば、他の図面を巻き込む事故は100%起きない。
2. 不可視化とメモリの強制解放
生成したインスタンスは `.Visible = False` で動作させ、処理終了後は容赦なく `.Quit` を叩き、変数に `Nothing` を代座させてCOMの参照カウンタを即座にゼロにする。
3. エラーハンドリングにおけるゾンビプロセス防止
予期せぬエラーでマクロが中断した際、タスクマネージャーに `acad.exe` の残骸(ゾンビプロセス)を残さないよう、必ず `On Error GoTo` のクリーンアップブロックでオブジェクトを破棄する構造を徹底する。

Public Sub SafeExecuteAutoCADTask()
Dim acadApp As AcadApplication
Dim acadDoc As AcadDocument

On Error GoTo CleanUp

‘ ── 1. 独立した新規インスタンスの生成(他者への干渉を完全排除) ──
Set acadApp = New AcadApplication
acadApp.Visible = True ‘ 必要に応じてFalse

Set acadDoc = acadApp.Documents.Add(“acad.dwt”)

‘ ── 2. 業務ロジックの実行 ──
‘ ここに極めて正確なエンティティ生成コードを記述

‘ ── 3. 正常終了時の保存と閉じる処理 ──
‘ acadDoc.SaveAs “C:\Output\Result.dwg”

CleanUp:
‘ ── 4. 厳格なメモリ最適化とオブジェクト破棄 ──
If Not acadDoc Is Nothing Then
‘ ドキュメントの明示的クローズ
‘ acadDoc.Close False
Set acadDoc = Nothing
End If

If Not acadApp Is Nothing Then
‘ アプリケーションの終了(必要に応じて)
‘ acadApp.Quit
Set acadApp = Nothing
End If

If Err.Number <> 0 Then
MsgBox “致命的なエラーが発生しました: ” & Err.Description, vbCritical
End If
End Sub

5. 結びにかえて

AutoCAD VBAはレガシーな技術と片付けられがちだが、OSのメモリ管理、COMのライフサイクル、そしてWindows APIの底力を理解していれば、これほど強靭で直感的な自動化ツールはない。

「どのインスタンスを掴んでいるか分からない」という恐怖心は、今日で終わりだ。プロセスを支配し、図面を支配せよ。それこそが、真のAutoCAD VBAエンジニアの姿である。

タイトルとURLをコピーしました