【実務・中級編】【上級】AcadApplication.GetInterfaceObjectを用いた「Sheet Set Manager」の操作:複数図面を跨ぐシート番号の一括更新と自動採番 – AutoCAD VBA解析バイブル

スポンサーリンク

AutoCAD VBAを掌握する極限の知見
第1回:`AcadApplication.GetInterfaceObject`によるSheet Set Manager(SSM)の完全制圧

開発プロジェクトの現場において、VBAは「おもちゃのマクロ」と揶揄されることがある。決まった図面を開き、図枠の文字を書き換えて保存する――そんな単体図面の自動化にとどまっているうちは、VBAの真価の1割も引き出せていない。

真のエンジニアが直面する課題は、「数百枚におよぶ大規模プロジェクトの整合性管理」だ。
特に、複数の図面ファイルを横断する「シート番号」や「ビューの同期」において、通常のAutoCADオブジェクトモデル(`AcadDocument`や`AcadModelSpace`)だけで立ち向かうのは、素手で要塞を攻略するようなものである。

今回は、AutoCADの隠れた巨大要塞である「Sheet Set Manager (SSM)」のCOMインターフェースをVBAから直接叩き、複数図面を跨ぐシート番号の一括自動採番システムを構築する極限の知見を授ける。

なぜ通常のDocument操作ではSSMを制御できないのか?

多くの初学者は、シートセット(`.dst`ファイル)内の情報を書き換える際、各図面を`Documents.Open`で次々と開き、属性(Attibute)をループで書き換えて保存するという非効率なアプローチをとる。

この手法には致命的な欠陥がある。
1. 圧倒的な処理コスト: 数百枚の図面をGUI上で開閉するため、膨大な時間がかかる。
2. トランザクションの欠如: 途中でエラー落ちした場合、データベースと図面の整合性が完全に破壊される。
3. SSMキャッシュの不整合: AutoCADの裏で管理されているDSTファイルのツリー構造と、図面のシートカスタムプロパティが乖離する。

AutoCADのAPIアーキテクチャにおいて、シートセットはAcadDocumentの寿命とは独立した「外部COMコンポーネント(AcSmComponents)」として存在している。これを正確に手懐ける唯一の鍵が、`AcadApplication.GetInterfaceObject` メソッドである。

アーキテクチャ設計:GetInterfaceObjectの正しいライフサイクル管理

`GetInterfaceObject`は、AutoCADのプロセス空間外、あるいは内部のCOMサーバーから特定のライブラリ(今回はSheet Set Managerのライブラリ)へのポインタを安全に取得するための強力なAPIだ。

ここで最も重要なのは、「COMオブジェクトの参照解放(Release)」である。VBAのガベージコレクションは非常に曖昧であり、特に外部COMコンポーネントを操作した後は、明示的に変数を `Nothing` に解放しなければ、AutoCADプロセス内にメモリリーク(ゾンビプロセス)が残り、次回実行時のクラッシュやファイルロックの原因となる。

プロフェッショナルは、例外発生時(Error Handler)であっても確実にオブジェクトを解放する堅牢な構造をコードに組み込む。

【プロダクションコード】シート番号自動採番・一括更新ツール

以下のコードは、指定したSheet Set (`.dst`ファイル) のルートからツリーを再帰的に走査し、特定の命名規則に従ってシート番号(Sheet Number)を美しく連番で再割り当てする実用モジュールだ。

Option Explicit

‘ =================================================================================
‘ 圧倒的な実務を支えるSSM一括自動採番エンジニアリングモジュール
‘ 前提条件: 参照設定不要(Late Bindingによる動的バインディングでバージョン差異を吸収)
‘ =================================================================================

Public Sub ExecuteSSMRenumbering()
Dim acadApp As AcadApplication
Set acadApp = ThisDrawing.Application

‘ DSTファイルのパスを指定(実務ではFileDialog等で動的取得を推奨)
Const dstPath As String = “C:\Projects\MegaStructure\Bridge_A.dst”

Dim dbxMgr As Object
Dim smDatabase As Object
Dim transLock As Long

On Error GoTo ErrorHandler

‘ 1. GetInterfaceObjectを用いたSSMデータベースマネージャーの召喚
‘ ※バージョン(AutoCAD 2024ならAcSmComponents.24.1等)に依存しないようProgIDを指定
Set dbxMgr = acadApp.GetInterfaceObject(“AcSmComponents.AcSmDatabaseMgr.1”)
If dbxMgr Is Nothing Then
Err.Raise 9999, “SSM_Init”, “Sheet Set ManagerのCOMコンポーネントを取得できませんでした。”
End If

‘ 2. DSTファイルをロックモードでオープン
‘ 第2引数の ‘True’ は排他制御(Read/Write)を意味する
Set smDatabase = dbxMgr.OpenDatabase(dstPath, True)
If smDatabase Is Nothing Then
Err.Raise 9998, “SSM_Open”, “指定されたDSTファイルを開けませんでした: ” & dstPath
End If

‘ 3. トランザクションロックの取得(整合性の担保)
transLock = smDatabase.LockDb(smDatabase.GetRoot())

‘ 4. ルートから再帰的にシートを走査し、自動採番を実行
Dim rootGroup As Object
Set rootGroup = smDatabase.GetRoot()

Dim currentNumber As Integer
currentNumber = 101 ‘ スタート番号の定義(例: 101から開始)

Call ProcessSheet(rootGroup, currentNumber)

‘ 5. 変更をデータベースにコミット
smDatabase.UnlockDb transLock, True ‘ True = 変更を保存

MsgBox “シート番号の再採番が正常に完了しました。”, vbInformation, “SSM Automation”

CleanUp:
‘ 確実にCOMオブジェクトを解放し、メモリリークを根絶する
On Error Resume Next
If Not smDatabase Is Nothing Then dbxMgr.CloseDatabase smDatabase
Set smDatabase = Nothing
Set dbxMgr = Nothing
Exit Sub

ErrorHandler:
MsgBox “致命的なエラーが発生しました: ” & Err.Description, vbCritical, “Critical Error”
If transLock <> 0 And Not smDatabase Is Nothing Then
smDatabase.UnlockDb transLock, False ‘ False = 変更を破棄してロールバック
End If
Resume CleanUp
End Sub

‘ =================================================================================
‘ 再帰的シートツリー走査プロシージャ
‘ =================================================================================
Private Sub ProcessSheet(ByVal currComponent As Object, ByRef seqNumber As Integer)
Dim iter As Object
Dim childComp As Object
Dim sheetObj As Object

‘ コンポーネントの型に応じた処理分岐
‘ AcSmEnumComponents を用いてサブセットおよびシートを列挙する
Set iter = currComponent.GetIterator()
If iter Is Nothing Then Exit Sub

Set childComp = iter.Next()
Do While Not childComp Is Nothing

‘ 育種されたコンポーネントが「サブセット(フォルダー構造)」か「シート」かを判定
‘ TypName または IID判定の代わりになんちゃって型判定を行う
If TypeName(childComp) = “IAcSmSubset” Then
‘ サブセットの場合は再帰呼び出し
Call ProcessSheet(childComp, seqNumber)

ElseIf TypeName(childComp) = “IAcSmSheet” Then
Set sheetObj = childComp

‘ プレフィックスを付与した美しいシート番号を生成 (例: “S-101”, “S-102″…)
Dim formattedNumber As String
formattedNumber = “S-” & Format(seqNumber, “000”)

‘ シートプロパティの書き換え
sheetObj.SetNumber formattedNumber

‘ デバッグ出力(イミディエイトウィンドウで確認可能)
Debug.Print “Updated Sheet: ” & sheetObj.GetName() & ” -> Number: ” & formattedNumber

seqNumber = seqNumber + 1
End If

Set childComp = iter.Next()
Loop
End Sub

プロジェクトリーダーからの実践的アドバイス

1. レイトバインディング(Late Binding)の徹底
上記のコードでは、あえて参照設定に依存しない `GetInterfaceObject` の文字列指定(レイトバインディング)を採用している。AutoCADのバージョン(2023, 2024, 2025…)が変わるたびにCOMライブラリのGUIDやバージョンサフィックスが微妙に変化するため、早期バインディング(Early Binding)を行うと環境移行時に一発でコードが破綻する。保守性を高めるためにはレイトバインディングが鉄則だ。

2. トランザクションのロールバック戦略
大規模な一括処理において、途中で例外が発生した際にデータベースが中途半端な状態でロックされると、次回からDSTファイルが破損扱いになる。必ず `UnlockDb transLock, False` をエラーハンドラ内に仕込み、トランザクションの原子性(Atomicity)を担保すること。

3. 図面ファイル(.dwg)側の同期
SSM側の番号を書き換えても、図面ファイル内の表題欄(レイアウトのビューポートや属性定義)が即座に連動しない場合がある。真に完璧なシステムを目指す場合、SSMのイベントリスナーやフィールド更新コマンド(`FIELDUUPDATE`)をバッチ実行するフェーズをこのプロシージャの直後に挟むと、実務で完璧に通用するツールへと昇華する。

手作業によるヒューマンエラーを根絶し、機械的な正確さでプロジェクトを統御せよ。これこそが、AutoCAD VBAを極めたエンジニアだけが到達できる領域である。

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