【上級プロフェッショナル】VBAからAutoCADのメニューバーとツールバーを動的にカスタマイズし、カスタムコマンドを登録する
AutoCADのUIカスタマイズといえば、長らくCUIファイル(MNU/MNS/MNX、そして近年のCUIX)の手動編集や、.NET APIによる高度な拡張が王道とされてきた。しかし、現場のシニアエンジニアやプラント・製造業のシステム管理者が直面するのは、「環境がバラバラのクライアント端末に対し、VBAの単一.dvbファイルだけで即座に、かつ動的にカスタムメニューとツールバーを構築・同期させたい」という泥臭くも切実な要求である。
現代のAutoCADアーキテクチャにおいて、VBAはレガシーな存在として片付けられがちだ。だが、オブジェクトモデルの根幹である `AcadApplication` と `AcadMenuBar`、そして `AcadToolbars` の挙動を完全に掌握していれば、CUIXのコンパイル地獄に陥ることなく、完全動的なUI構築が可能となる。
本稿では、AutoCAD VBAを用いたメニューバー・ツールバーの動的生成、およびマクロコマンドの登録において、メモリ管理の罠やAPIの挙動不整合を回避し、実務に耐えうる堅牢なシステムを構築するための極限の知見を公開する。
—
1. AutoCAD UIオブジェクトモデルの深層とライフサイクル
VBAからAutoCADのUIを操作する場合、避けて通れないのが「COMオブジェクトの参照管理」と「AutoCADの内部ドキュメント状態(MDI環境)」のジレンマだ。
多くの開発者が犯す最大の過ちは、オブジェクト変数への代入時に適切な解放を行わず、AutoCADプロセス内にCOMラッパーの残骸を残してしまうことである。特に `AcadToolbarItem` や `AcadPopupMenu` のようなコレクションアイテムを走査・削除する際、`.Delete` メソッドの実行タイミングを誤ると、メモリリークだけでなく、最悪の場合AutoCAD本体の強制終了(致命的なエラー)を引き起こす。
UI構築における鉄則
1. メニューバーは常に `ThisDrawing.Application.MenuBar` から操作する:ドキュメントスコープではなく、アプリケーションスコープで一元管理する。
2. ツールバーは `ThisDrawing.Application.Toolbars` を基準にする:図面間での競合を防ぐため、常に `AcadApplication` 起点でイテレーションを回す。
3. 完全なオブジェクトの解放(Destruction):参照した各ポップアップメニューやツールバーアイテムは、処理終了時に必ず `Nothing` を代入し、COM参照カウンタをデクリメントする。
—
2. 実装コード:動的メニュー&ツールバー生成エンジンの構築
以下のコードは、指定したカスタムコマンド群を読み込み、既存の同名メニュー/ツールバーを安全にクリーンアップした上で、完全に動的に再構築するプロフェッショナル向けの実装例である。
Option Explicit
‘ ==============================================================================
‘ モジュール名: modUIController
‘ 概要: AutoCADのメニューバーとツールバーを動的に構築・制御するエンジン
‘ 著作権: チーフアーキテクト専用知見コード
‘ ==============================================================================
Public Sub BuildCustomCADEnvironment()
On Error GoTo ErrorHandler
Dim acadApp As AcadApplication
Set acadApp = ThisDrawing.Application
‘ 1. 既存の同名カスタムUIの安全な削除(競合防止)
Call RemoveExistingCustomUI(acadApp, “MyCorp Tools”)
‘ 2. メニューバーの動的生成
Call CreateDynamicMenu(acadApp, “MyCorp Tools”)
‘ 3. ツールバーの動的生成
Call CreateDynamicToolbar(acadApp, “MyCorp Tools”)
‘ 4. 変更の強制フラッシュ
acadApp.Update
MsgBox “カスタムUIの構築が正常に完了しました。”, vbInformation, “UI Optimizer”
GoTo CleanUp
ErrorHandler:
MsgBox “致命的なエラーが発生しました: ” & Err.Description, vbCritical, “UI Error”
CleanUp:
‘ 厳格なオブジェクト解放
Set acadApp = Nothing
End Sub
‘ ——————————————————————————
‘ 既存UIのクリーンアップ(メモリリーク防止と重複回避)
‘ ——————————————————————————
Private Sub RemoveExistingCustomUI(ByVal app As AcadApplication, ByVal menuName As String)
On Error Resume Next
‘ メニューバーからの削除
Dim i As Long
For i = app.MenuBar.Count To 1 Step -1
If app.MenuBar(i).Name = menuName Then
app.MenuBar(i).Remove
Exit For
End If
Next i
‘ ツールバーからの削除
Dim tbar As AcadToolbar
For Each tbar In app.Toolbars
If tbar.Name = menuName Then
tbar.Delete
Exit For
End If
Next tbar
Set tbar = Nothing
On Error GoTo 0
End Sub
‘ ——————————————————————————
‘ メニューバーの動的生成とコマンドバインド
‘ ——————————————————————————
Private Sub CreateDynamicMenu(ByVal app As AcadApplication, ByVal menuName As String)
Dim currMenu As AcadPopupMenu
Set currMenu = app.MenuBar.Add(menuName)
‘ メニュー項目の追加 (Caption, Macro)
‘ ※注意: マクロ内の特殊文字(スペースやESCなど)は正確にエスケープすること
Dim subItem1 As AcadPopupMenuItem
Set subItem1 = currMenu.AddMenuItem(currMenu.Count + 1, “一括図面パージ”, “^C^C_PURGE;_All;;_No; “)
Dim subItem2 As AcadPopupMenuItem
Set subItem2 = currMenu.AddMenuItem(currMenu.Count + 1, “属性抽出エクスポート”, “^C^C-ATTOUT; “)
‘ セパレータの挿入例
Dim sepItem As AcadPopupMenuItem
Set sepItem = currMenu.AddSeparator(currMenu.Count + 1)
Dim subItem3 As AcadPopupMenuItem
Set subItem3 = currMenu.AddMenuItem(currMenu.Count + 1, “社内標準レイヤ適用”, “^C^C-LAYER;_M;STD_LAYER;; “)
‘ 解放
Set subItem1 = Nothing
Set subItem2 = Nothing
Set subItem3 = Nothing
Set sepItem = Nothing
Set currMenu = Nothing
End Sub
‘ ——————————————————————————
‘ ツールバーの動的生成とボタン割り当て
‘ ——————————————————————————
Private Sub CreateDynamicToolbar(ByVal app As AcadApplication, ByVal tbarName As String)
Dim currTbar As AcadToolbar
Set currTbar = app.Toolbars.Add(tbarName)
‘ ツールバーボタンの追加 (Index, Name, HelpString, Macro)
Dim btn1 As AcadToolbarItem
Set btn1 = currTbar.AddToolbarButton(0, “PurgeBtn”, “図面内の不要定義を全て削除します”, “^C^C_PURGE;_All;;_No; “)
Dim btn2 As AcadToolbarItem
Set btn2 = currTbar.AddToolbarButton(1, “LayerBtn”, “社内標準レイヤを強制展開します”, “^C^C-LAYER;_M;STD_LAYER;; “)
‘ ツールバーの表示状態を確実にアクティブにする
currTbar.Visible = True
‘ 解放
Set btn1 = Nothing
Set btn2 = Nothing
Set currTbar = Nothing
End Sub
—
3. レガシー環境・システム間連携における極限の知見
ここからは、表面上のAPIリファレンスには決して載っていない、過酷な現場を生き抜くための「実践的ノウハウ」を伝授する。
A. マクロ文字列(Macro String)における制御コードの罠
VBAから `AddMenuItem` や `AddToolbarButton` に渡すマクロ文字列は、AutoCADのコマンドラインに対するキー入力のシミュレーションである。
- スペースは「Enterキー」を意味するため、コマンド名の区切りには厳密なスペースとセミコロン `;` の使い分けが必要。
- キャンセルを確実に行うための `^C^C` プレフィックスは、現在実行中のコマンドを強制中断させるために必ず先頭に付与すること。特にMDI環境下では、前回のコマンドが予期せぬアクティブ状態で残存しているケースが多いため、この防衛策がシステムの安定性を左右する。
B. CUIXファイルとの競合および権限問題
近年のAutoCAD(2010以降)は、UIの大部分をCUIXファイルで管理している。VBAから動的に作成したメニューやツールバーは、標準ではメインCUIXではなく「部分CUIX(または一時的なアプリケーションメモリ空間)」として展開される。
そのため、AutoCADを再起動すると設定が消失する場合がある。
これを完全に制御・永続化したいシニアエンジニアは、以下の設計アプローチをとるべきである。
1. 起動時自動実行(AutoLISPまたはVBAのAcadStartupイベント)を利用する:
AutoCADが立ち上がるたびに、前述の `BuildCustomCADEnvironment` プロシージャをサイレント実行し、常に最新のUI状態をオンザフライで構築する。これにより、CUIXファイルのバージョン管理地獄から完全に解放される。
2. 権限昇格(UAC)の回避:
Program Files配下のCUIXを書き換えるアプローチは、セキュリティポリシーの厳しい企業環境では御法度である。VBAによるメモリ上およびユーザープロファイル領域でのUI構築手法は、権限昇格を必要としない極めてクリーンなシステム間連携の解法となる。
C. 複数ドキュメント(MDI)環境におけるスコープの厳守
`ThisDrawing` というグローバル参照は極めて便利だが、ユーザーが複数枚の図面を開いている状態(MDI)では、どのドキュメントからスクリプトがキックされたかによって挙動が不安定になることがある。
UIのカスタマイズはアプリケーション全体のグローバルな変更であるため、コード内では常に `ThisDrawing.Application` (すなわち `AcadApplication`)を起点とし、個別の図面オブジェクト(`AcadDocument`)に依存しない構造を貫くこと。これがマルチドキュメント環境におけるクラッシュを防ぐ唯一の盾となる。
—
総括
VBAはレガシーと言われて久しい。しかし、AutoCADのCOMインターフェースの深部を理解したエンジニアの手にかかれば、VBAは「最も手軽に、最も環境依存を排除してCADの挙動を支配できる強力なオーケストレーションツール」へと変貌する。
今回解説した動的UI構築手法をあなたの業務システムに組み込むことで、クライアント端末の環境差異に悩まされる日々から完全に脱却し、真に堅牢なAutoCAD自動化基盤を築き上げてほしい。
