Visio VBAを掌握する極限の知見:Documentのソリューションセクションで内部状態を神速かつ安全に管理する法
業務効率化マクロの開発において、常に付きまとう悩ましい問題がある。
それは、「作成したツールの実行履歴、バージョン管理情報、あるいはライセンスの有効性といった『メタデータ』を、どこに保持すべきか」という点だ。
一般的な開発者は、カスタム・ドキュメント・プロパティや、ダミーのシェイプをどこかのページに隠してデータを押し込もうとする。だが、チーフアーキテクトの視点から言わせてもらえば、それは悪手だ。ユーザーがプロパティ画面を開いて値を書き換えてしまったり、不要なシェイプと勘違いしてデリート(削除)されてしまえば、ツールは一瞬で崩壊する。
Visioの深淵を覗いた者だけが知る、最もエレガントで堅牢な解決策——それこそが、`Document` オブジェクトの ShapeSheet 内にある「ソリューションセクション(SolutionXRef)」の活用だ。
今回は、この不可視かつ安全な領域をVBAで完全に制御し、プロダクション環境に耐えうるメタデータ管理基盤を構築する極意を伝授する。
—
1. なぜ「ソリューションセクション」なのか?
Visioのドキュメント(`Document` オブジェクト)自体は、実は見えないShapeSheetを持っている。そして、その中にカスタムデータを安全に格納するための拡張領域が存在する。
これを活用するメリットは圧倒的だ。
- ユーザーからの完全な不可視性: 通常のUIやプロパティウィンドウからは一切アクセス・変更できないため、改ざんや誤削除を防げる。
- ファイル運用の簡便さ: 外部のJSONやINIファイル、DB接続に依存せず、`.vsdm` ファイル単体の中に状態がカプセル化される。
- 揮発性の排除: ドキュメントのライフサイクルと完全に同期するため、ファイル移動や別名保存を行ってもデータが追従する。
—
2. アーキテクチャ設計:安全なデータアクセスの要件
プロダクションコードとして組み込むにあたり、以下の設計思想を厳守してほしい。
1. セクションの動的担保(Self-Healing):
初回実行時や、別環境から持ち込まれたドキュメントには目的のセクションが存在しない可能性がある。コード側で存在チェックを行い、なければ自動生成する「自己修復型」のロジックにする。
2. エラーハンドリングの網羅:
VisioのShapeSheet操作は、セルのロックや不正なインデックス指定時に重篤なランタイムエラーを吐く。トランザクション的な視点を持って確実にトラップする。
—
3. プロダクションコード:SolutionXRef 制御モジュール
以下のコードは、アクティブなドキュメントのソリューションセクションに対して、ツールの実行履歴やメタデータを安全に読み書きするための実践的なVBAクラス/モジュールである。
Option Explicit
‘ ==============================================================================
定数定義
‘ ==============================================================================
Private Const SECTION_NAME As String = “SolutionXRef”
Private Const ROW_NAME As String = “ToolMetadata”
Private Const CELL_DATA_KEY As String = “Prop1”
‘ ==============================================================================
‘ 概要: ドキュメントのソリューションセクションに内部状態(JSON等)を書き込む
‘ 引数: i_DataValue – 保存したい文字列(JSON形式などを推奨)
‘ ==============================================================================
Public Sub SaveToolMetadata(ByVal i_DataValue As String)
Dim vsoDoc As Visio.Document
Set vsoDoc = ActiveDocument
On Error GoTo ErrorHandler
‘ 1. セクションおよび行の存在確認と自動生成
Call EnsureSolutionRowExists(vsoDoc)
‘ 2. セルへの書き込み
‘ SolutionXRefセクション内のカスタムセルに値を格納
Dim vsoCell As Visio.Cell
Set vsoCell = vsoDoc.CellsSRC(Visio.visSectionSolution, 0, Visio.visCellSolutionNavUrl) ‘ ※用途に合わせたセルを使用
‘ 実務では FormulaU プロパティを使用し、文字列はダブルクォーテーションで囲む
vsoCell.FormulaU = “””” & Replace(i_DataValue, “”””, “”””””) & “”””
Exit Sub
ErrorHandler:
MsgBox “メタデータの保存に失敗しました: ” & Err.Description, vbCritical, “SolutionXRef Manager”
End Sub
‘ ==============================================================================
‘ 概要: ドキュメントのソリューションセクションから内部状態を読み込む
‘ 戻り値: 保存されていた文字列
‘ ==============================================================================
Public Function LoadToolMetadata() As String
Dim vsoDoc As Visio.Document
Set vsoDoc = ActiveDocument
On Error GoTo ErrorHandler
‘ セクションが存在しない場合は空文字を返す
If Not HasSolutionRow(vsoDoc) Then
LoadToolMetadata = “”
Exit Function
End If
Dim vsoCell As Visio.Cell
Set vsoCell = vsoDoc.CellsSRC(Visio.visSectionSolution, 0, Visio.visCellSolutionNavUrl)
‘ ResultStrで文字列として安全に取得 (Visio.visNone = 0)
LoadToolMetadata = vsoCell.ResultStr(Visio.visNone)
Exit Function
ErrorHandler:
LoadToolMetadata = “”
MsgBox “メタデータの読み込みに失敗しました: ” & Err.Description, vbCritical, “SolutionXRef Manager”
End Function
‘ ==============================================================================
‘ 内部処理: セクションと行が存在するか確認し、なければ作成する
‘ ==============================================================================
Private Sub EnsureSolutionRowExists(ByRef oDoc As Visio.Document)
If Not HasSolutionRow(oDoc) Then
‘ SolutionXRefセクションが存在しない、または行がない場合は追加
‘ 注意: Visioの仕様上、セクションの有無を厳密に判定して行を追加する
On Error Resume Next
oDoc.AddSection Visio.visSectionSolution
oDoc.AddRow Visio.visSectionSolution, Visio.visRowLast, 0
On Error GoTo 0
End If
End Sub
‘ ==============================================================================
‘ 内部処理: SolutionXRefに行が存在するかを判定
‘ ==============================================================================
Private Function HasSolutionRow(ByRef oDoc As Visio.Document) As Boolean
HasSolutionRow = False
On Error Resume Next
Dim rowCount As Integer
rowCount = oDoc.SectionExists(Visio.visSectionSolution, Visio.visFCtlAddbAsView)
If rowCount Then
If oDoc.RowExists(Visio.visSectionSolution, 0, Visio.visFmlaIgnored) Then
HasSolutionRow = True
End If
End If
On Error GoTo 0
End Function
—
4. チーフアーキテクトからの実務アドバイス
この手法を実務の現場に導入する際、以下のポイントを必ず心に留めておいてほしい。
- JSON化して格納せよ
単一の値だけでなく、`{“LastRunUser”:”Yamada”, “Version”:”1.2.0″, “LicenseValid”:true}` のようにJSON形式にシリアライズして格納することを強く推奨する。拡張性が段違いになる。
- マクロ有効ファイル(.vsdm)での運用
当然ながら、このコードを実行・保存するにはドキュメント形式がマクロ有効Visio図面である必要がある。テンプレート配布の段階でフォーマットを固定化しておこう。
自作ツールの裏側でスマートに状態を管理し、ユーザーの介入によるバグを根絶する。この「ソリューションセクション活用術」をあなたの武器庫に加え、ワンランク上のソリューション開発を実現してほしい。
