Visio VBAの深淵:Application.DoCmdが切り拓く「自動化の聖域」
Visioのオブジェクトモデルは一見完成されているように見えるが、実務の極限に挑むエンジニアなら誰しも一度は壁に突き当たる。「なぜ、この手動操作をAPIで直接呼び出せないのか」と。
プロパティやメソッドが用意されていない機能、あるいは複雑なダイアログを伴う標準操作。これらを解決するための「最後の切り札」が `Application.DoCmd` である。今回は、単なるコマンドIDの羅列ではない、システムアーキテクトの視点からの「DoCmd」の掌握術を伝授する。
—
1. DoCmdの本質:ブラックボックスとの対話
`Application.DoCmd` は、Visioの内部コマンドバッファに対して直接IDを送り込む非同期的な命令だ。これは公開API(Application.ActivePage等)とはレイヤーが異なる。
注意すべき鉄則:
- 非同期実行: `DoCmd` を投げた直後に次の処理を書くと、Visio側の処理が完了していない状態でコードが先行し、予期せぬエラー(特に `Invalid Object` や `COMException`)を誘発する。
- UIスレッドの占有: ダイアログが表示されるコマンドの場合、コードの実行はユーザーが操作を完了させるまで待機する。
—
2. 実践:DoCmdによる自動化コードの極意
単に `visCmd…` を叩くのは素人だ。真の自動化では、Windows APIによるウィンドウ制御と組み合わせることで、Visioを「無人操作」させることが可能になる。
以下は、標準機能の「図面の名前を付けて保存」を呼び出し、特定のファイル形式へ強制的に誘導する際のアーキテクチャ例である。
Option Explicit
‘ メモリ管理の鉄則:オブジェクトは明示的に解放する
‘ DoCmd利用時は、特にVisioのUIの状態が不安定になりやすいため、
‘ ラッパーメソッドでエラーハンドリングを厳格化する。
Public Sub ExecuteSaveAsCommand()
On Error GoTo ErrHandler
‘ 1. コマンドの発行
‘ visCmdFileSaveAs (1002) を直接実行
Application.DoCmd visCmdFileSaveAs
‘ 2. ここで重要なのは、ダイアログが開いた後の「待ち時間」の制御
‘ DoEventsでVisioのUIスレッドに制御を戻す
DoEvents
Exit Sub
ErrHandler:
Debug.Print “Error: ” & Err.Number & ” – ” & Err.Description
‘ 適切なオブジェクト解放処理
End Sub
【極限の知見】コマンドIDの特定方法
公開されている `Visio.VisCmdIDs` に含まれない隠しコマンドを探るには、Visioの「マクロの記録」を信じてはならない。あれは出力が冗長すぎる。`VisEventProc` を実装し、全てのイベントを監視して標準操作時のコマンドIDをログに出力するデバッグ環境を構築することが、アーキテクトとしての最初の仕事だ。
—
3. パフォーマンスとメモリの最適化:レガシーを殺すな
VisioのVBAは、COMのラッパーに過ぎない。特に `DoCmd` を多用するループ処理は、メモリリークの温床となる。
1. オブジェクトの破棄: `Set obj = Nothing` を徹底せよ。特に `Selection` オブジェクトや `Shape` オブジェクトを `DoCmd` 実行中に参照し続けると、Visioの内部ポインタが破損することがある。一度コマンドを投げる前に、参照をクリアする設計が肝要だ。
2. イベントの停止: `Application.ScreenUpdating = False` だけでなく、`Application.EventsEnabled = False` を併用せよ。`DoCmd` は内部的に多数のイベントを誘発する。不要なイベント連鎖を防ぐことが、安定稼働の要である。
—
4. システム間連携の限界突破
Visioを「描画エンジン」として外部から操作する場合、`DoCmd` は強力な武器になる。例えば、C#のWPFアプリケーションからVisioをホストし、`DoCmd` で「グループ化」や「整列」を実行させる場合、`System.Runtime.InteropServices` を介したCOMのマーシャリングに注意が必要だ。
特に、`DoCmd` が内部的にモーダルダイアログを起動する場合、親プロセス(C#側)のウィンドウハンドルを正しく渡しておかないと、Visioが「バックグラウンドの墓場」で立ち往生する。
// C#側での呼び出しイメージ
// Visioのインスタンスを生成し、ウィンドウハンドルを同期させる
// DoCmd実行前には必ず、対象図面をActiveDocumentにセットせよ
visioApp.ActiveWindow.Activate();
visioApp.DoCmd((short)Visio.VisCmdIDs.visCmdToolsMacro);
—
アーキテクトからの最終警告
`DoCmd` は強力だが、あくまで「VisioのUI操作の代替」である。
可能な限り、`Shape.CellsU` や `Application.Addons` を駆使し、コマンドIDに頼らない論理的な処理を優先せよ。`DoCmd` は最後の手段(Last Resort)であるべきだ。
しかし、どうしても越えられない壁にぶつかった時、この記事で記した知見が、君の自動化システムを「動くもの」から「止まらないシステム」へと昇華させることを約束する。
Visioという古い巨人(巨人)と踊る準備はいいか?
コードは常に、潔く、そして冷徹に書け。
