【Visio VBA極限解説】外部依存を断つ!`Document.Masters`によるステンシル図形動的ロードの極意
こんにちは。開発プロジェクトを率いるチーフアーキテクトの私だ。
Visioを使った自動化ツールを開発する際、多くのエンジニアが最初に直面し、そして頭を抱える問題がある。それは「ステンシルファイル(.vssx)のパス依存問題」だ。
「共有フォルダに置いたステンシルのパスが変わった」「担当者のPCが変わったら図形が読み込めなくなった」「実行時エラー `1004` が出て止まる」。
こんな場当たり的な不具合対応に、貴重な開発工数を溶かしていないか?
実務で耐えうる堅牢なツールを構築したいなら、外部の `.vssx` ファイルに依存した `Drop` 処理は今すぐ捨てるべきだ。
今回は、Visioのオブジェクトモデルの心臓部である `Document.Masters` を完全掌握し、必要なマスターシェイプをプログラム側で動的にインポート・生成する「真にポータブルな自動化アーキテクチャ」を伝授しよう。
—
1. なぜ「外部パス依存」のコードは実務で破綻するのか?
多くの解説書やネットの記事では、次のようなコードが平然と紹介されている。
‘ 【悪夢のアンチパターン】絶対にやってはいけないコード
Dim stnPath As String
stnPath = “C:\Projects\Shapes\MyStencil.vssx”
‘ 外部ステンシルを開いて図形をドロップ
Dim visStencil As Document
Set visStencil = Documents.OpenEx(stnPath, visOpenRO + visOpenHidden)
visStencil.Masters.ItemU(“MyShape”).Drop ActivePage, 5, 5
visStencil.Close
このアプローチがなぜプロの現場で嫌われるのか。理由は明確だ。
1. 環境依存の脆弱性: 開発者のローカルPC、共有サーバー、本番環境でパスが一致することは稀である。環境変数を駆使しても、保守フェーズで確実に破綻する。
2. ファイルロックと排他制御: 複数ユーザーが同時にツールを使用する際、共有 `.vssx` ファイルが開けない、あるいは競合による書き込みエラーが発生する。
3. ドキュメントの肥大化と孤立: 成果物である `.vsdx` ファイルを開いた際、元ステンシルとのリンクが切れた瞬間に図形が「ゾンビ化」する。
究極の解決策:マスターシェイプの「内製化(Documentへの埋め込み)」
Visioのドキュメント(`Document` オブジェクト)は、自身の中に独自のマスターシェイプコレクション(`ActiveDocument.Masters`)を保持する能力を持っている。
つまり、ツール起動時(あるいは初回実行時のみ)に、外部ステンシルから必要なマスターシェイプを `ActiveDocument.Masters.AddU` を使って自ドキュメント内にコピー(クローン)し、以後はドキュメント内部のマスターだけを参照して図形を生成するのだ。
この設計にすれば、配布するのはマクロを内蔵した `.vsdm`(または `.vsdx`)ファイル単体だけで完結する。外部の `.vssx` を持ち歩く必要は一切なくなる。
—
2. アーキテクチャ設計:堅牢な動的ロードの流れ
今回構築するロジックのライフサイクルは以下の通りだ。
1. マスター存在チェック: 描画先ドキュメント(`ActiveDocument.Masters`)に、目的のマスターが既に存在するか確認する。
2. 条件付きインポート: 存在しない場合のみ、リソース(あるいは一時的に開いたステンシル)からマスターをドキュメントのマスターコレクションに追加する。
3. 図形生成(Drop): ドキュメント内のマスターから安全に図形をインスタンス化する。
実務では、ステンシルファイルを社内ニッチな共有パスから読み込むのではなく、VBAプロジェクトの「アドオン」や「バイナリリソース」として扱う、あるいは安全なローカルキャッシュに一時展開するアプローチが最も堅牢だ。今回は、最も汎用性が高い「一時的にステンシルを開いてマスターをドキュメントに取り込む」スマートな実装コードを公開しよう。
—
3. 【プロダクションコード】コピペで動く堅牢な動的ロードモジュール
以下のコードは、エラーハンドリング、オブジェクトのクリーンアップ、メモリ管理(COMの解放)を徹底的に考慮したプロダクション品質のVBAコードだ。そのままプロジェクトに組み込んで即戦力として使ってほしい。
Option Explicit
‘ ==============================================================================
‘ 担当: チーフアーキテクト
‘ 概要: 外部ステンシルに依存せず、Document.Mastersへマスターを動的ロードして図形を生成する
‘ ==============================================================================
Public Sub CreateShapeDynamically()
Dim targetStencilPath As String
Dim masterName As String
Dim targetPage As Visio.Page
Dim targetMaster As Visio.Master
Dim generatedShape As Visio.Shape
‘ — 1. 設定値の定義 (実際のパスや名称に書き換えてください) —
‘ ※理想はVBA内でバイナリを保持するか、実行時に安全なパスから取得すること
targetStencilPath = “C:\EnterpriseTools\Templates\NetworkShapes.vssx”
masterName = “Router” ‘ ステンシル内のマスターシェイプ名(UIVersion/UIName)
Set targetPage = ActivePage
On Error GoTo ErrorHandler
‘ — 2. ターゲットマスターが既にドキュメント内に存在するか確認 —
Set targetMaster = GetMasterFromDocument(ActiveDocument, masterName)
‘ — 3. 存在しない場合は外部ステンシルからインポート —
If targetMaster Is Nothing Then
Debug.Print “マスター [” & masterName & “] がドキュメントに見つかりません。外部からロードします。”
Set targetMaster = ImportMasterFromStencil(ActiveDocument, targetStencilPath, masterName)
End If
‘ — 4. ドキュメント内のマスターから図形をドロップ —
‘ 外部ステンシルを開きっぱなしにする必要はないため、メモリとファイルロックの懸念が消滅する
Set generatedShape = targetPage.Drop(targetMaster, 4.0, 5.0)
‘ プロパティの設定例
generatedShape.Text = “Core-Router-01”
MsgBox “図形の動的生成に成功しました!”, vbInformation, “Visio自動化”
CleanExit:
‘ オブジェクトの参照解放(VBAにおけるメモリリーク防止の定石)
Set generatedShape = Nothing
Set targetMaster = Nothing
Set targetPage = Nothing
Exit Sub
ErrorHandler:
MsgBox “致命的なエラーが発生しました。” & vbCrLf & _
“エラー番号: ” & Err.Number & vbCrLf & _
“説明: ” & Err.Description, vbCritical, “System Error”
Resume CleanExit
End Sub
‘ ==============================================================================
‘ 補助関数: ドキュメントのMastersコレクションから指定名のマスターを取得する
‘ ==============================================================================
Private Function GetMasterFromDocument(ByVal doc As Visio.Document, ByVal mName As String) As Visio.Master
Dim mst As Visio.Master
On Error Resume Next
‘ 名古屋の一致(言語非依存のUIVersion名またはNameUを想定)
Set mst = doc.Masters.ItemU(mName)
If Err.Number <> 0 Then
Set mst = Nothing
End If
On Error GoTo 0
Set GetMasterFromDocument = mst
End Function
‘ ==============================================================================
‘ 補助関数: 外部ステンシルから指定マスターを対象ドキュメントへコピーする
‘ ==============================================================================
Private Function ImportMasterFromStencil(ByVal targetDoc As Visio.Document, ByVal stencilPath As String, ByVal mName As String) As Visio.Master
Dim sourceStencil As Visio.Document
Dim srcMaster As Visio.Master
Dim importedMaster As Visio.Master
‘ ファイルの存在確認
If Dir(stencilPath) = “” Then
Err.Raise 53, “ImportMasterFromStencil”, “ステンシルファイルが見つかりません: ” & stencilPath
End If
‘ 外部ステンシルを読み取り専用・非表示で開く
Set sourceStencil = Documents.OpenEx(stencilPath, visOpenRO + visOpenHidden)
‘ 外部ステンシルからマスターを取得
On Error Resume Next
Set srcMaster = sourceStencil.Masters.ItemU(mName)
On Error GoTo 0
If srcMaster Is Nothing Then
sourceStencil.Close
Err.Raise 91, “ImportMasterFromStencil”, “ステンシル内に指定されたマスターが存在しません: ” & mName
End If
‘ ★ここが核心:外部マスターを対象ドキュメントのMastersに複製(Add)する
‘ これにより、外部ファイルへの依存関係がこの瞬間に断ち切られる
Set importedMaster = targetDoc.Masters.AddU(srcMaster)
‘ 外部ステンシルを閉じる(忘れるとファイルロックやメモリ圧迫の原因になる)
sourceStencil.Close
Set sourceStencil = Nothing
Set ImportMasterFromStencil = importedMaster
End Function
—
4. チーフアーキテクトからの実践的なアドバイス
この実装を採用するにあたり、現場のエンジニアに伝えておきたいポイントがいくつかある。
① 名前の衝突(NameU と Name)に気をつけろ
Visioのマスター操作において、日本語ロケール名(`Name`)で指定すると、英語環境やOSの設定変更時にコードがクラッシュする原因になる。必ず `ItemU` および `NameU`(言語非依存のユニーク名) を使用する設計を徹底してほしい。
② ファイルロックの罠
`Documents.OpenEx` でステンシルを開いた後、何らかのエラーでサブルーチンが中断すると、ステンシルファイルがロックされたままになる。必ず `On Error GoTo CleanExit` のような構造化例外処理を書き、異常終了時でも確実に `sourceStencil.Close` が走る、あるいはVBAの実行コンテキストが安全に回収される担保を作ること。
③ さらなる高みへ:バイナリ内蔵型ツール
極限までポータビリティを高めたい場合、初回起動時にVBAのコード内(あるいはワークシートの隠しセル、カスタムXMLパーツなど)に保持したバイナリデータを一時フォルダに `.vssx` として書き出し、そこから `ImportMasterFromStencil` を実行する手法もある。ここまでやれば、ユーザーは「マクロ付きVisioファイル単体」を受け取るだけで、他のいかなるリソースファイルも必要としない、完璧なスタンドアロンツールが完成する。
—
総括
Visio VBAにおけるオブジェクトモデルの美しさは、「ドキュメントが単なるキャンバスではなく、自己完結型のコンテナである」という点にある。
外部へのパス依存を断ち切り、`Document.Masters` を通じた動的ロードの設計思想を身につけたあなたなら、もう環境差異による不具合に怯える必要はない。
プロフェッショナルとして、美しく、堅牢で、保守性の高い真の自動化ソリューションを現場に提供してくれ。健闘を祈る。
