Visio Documentメタデータの極限管理:CustomDocumentPropertiesによる版数・監査証跡の完全自動化
Visio VBAにおける最大の悪夢は、何世代にもわたって野良改修された図面の乱立と、それによる「どれが最新の図面か分からない」という現場の混乱である。ファイル名に `_v2_final_revised.vsdx` などと泥臭いバージョンサフィックスを付与する時代は、今日で終わりにするべきだ。
プロフェッショナルなエンタープライズ環境において、図面は単なる「絵」ではなく、厳格な構成管理下に置かれるべき「データアセット」である。今回は、Visioのドキュメントオブジェクトが内包する `CustomDocumentProperties` を極限まで活用し、図面の開き初め(Open)および保存時(BeforeSave)に、作成部署・版数・ハッシュ値、さらにはシステム間連携用のメタデータを不可逆的に同期・統御するアーキテクチャを解説する。
—
1. Documentオブジェクトモデルとライフサイクルの真実
多くのVBAプログラマは、`ActiveDocument` という甘美なグローバルショートカットに依存し、メモリリークやコンテキストロストを引き起こす。しかし、真のエンジニアは `ThisDocument` モジュールと、イベントドリブンなライフサイクルを完全に掌握していなければならない。
メモリの重みとオブジェクト解放の鉄則
VisioのCOMコンポーネントモデルにおいて、オブジェクトの参照を解放し忘れることは、VSA(Visual Studio for Applications)のヒープ領域におけるサイレントキラーとなる。特に `Document` から `CustomProperty`(Visioの内部実装では `MasterShortcut` や古いCOM構造の残滓を引きずる厄介なコレクション)にアクセスする際、不適切な変数保持はExcelやVisioプロセスのゾンビ化を招く。
以下の原則を遵守せよ:
1. `ActiveDocument` を使うな。 イベントハンドラの引数として渡される `Doc As Visio.Document` を常にスコープの起点とする。
2. Variant型の遅延バインディングに頼るな。 `CustomProperty` へのアクセスは、型安全なオブジェクト参照を明示的に取得し、処理後は即座に `Nothing` を代入して参照カウントをデクリメントする。
—
2. 実装:エンタープライズグレード・メタデータ自動管理エンジン
以下のコードを、対象となるVisioステンシルまたは図面テンプレート(`.vstx` / `.vsdm`)の `ThisDocument` モジュールに直接実装せよ。
このアーキテクチャでは、ドキュメントオープン時に環境変数やActive Directory(またはレジストリ)から実行者情報を取得し、カスタムプロパティが存在しない場合は自動生成、存在する場合は版数をインクリメント、あるいはタイムスタンプを強制同期する。
Option Explicit
‘ ==============================================================================
‘ チーフアーキテクト設計:Visio Documentメタデータ統御モジュール
‘ 対象: ThisDocument (Class Module)
‘ ==============================================================================
‘ 定数定義
Private Const PROP_VERSION As String = “DrawingVersion”
Private Const PROP_AUTHOR As String = “LastModifiedBy”
Private Const PROP_DATE As String = “LastModifiedDate”
Private Const PROP_DEPT As String = “OwningDepartment”
Private Const PROP_SCHEMA As String = “MetadataSchemaVersion”
Private Sub Document_DocumentOpened(ByVal Doc As Visio.Document)
On Error GoTo ErrorHandler
‘ 読み取り専用ファイルの場合はメタデータ更新をスキップ(競合防止)
If Doc.ReadOnly Then Exit Sub
Dim props As Visio.CustomProperties
Set props = Doc.CustomProperties
‘ スキーマバージョンの初期化・検証
Call EnsureProperty(props, PROP_SCHEMA, “1.00”, visPropTypeString, “メタデータ構造バージョン”)
‘ 版数プロパティの初期化(未存在なら “1.00”)
Call EnsureProperty(props, PROP_VERSION, “1.00”, visPropTypeString, “図面リビジョン”)
‘ 最終更新者と日時の更新
Dim currentUser As String
currentUser = GetNetworkUserName() & ” (” & Environ(“USERDOMAIN”) & “)”
Call SetPropertyValue(props, PROP_AUTHOR, currentUser)
Call SetPropertyValue(props, PROP_DATE, Format(Now, “yyyy-mm-dd hh:nn:ss”))
‘ 変更フラグをクリアして無駄な保存プロンプトを出さない
‘ (必要に応じてドキュメントのDirtyフラグを制御)
GoTo CleanUp
ErrorHandler:
MsgBox “Document_Opened イベントでエラーが発生しました: ” & Err.Description, vbCritical, “メタデータ管理エンジン”
CleanUp:
Set props = Nothing
End Sub
Private Sub Document_BeforeSave(ByVal Doc As Visio.Document)
On Error GoTo ErrorHandler
If Doc.ReadOnly Then Exit Sub
Dim props As Visio.CustomProperties
Set props = Doc.CustomProperties
‘ 保存直前に最終更新タイムスタンプを強制上書き
Call SetPropertyValue(props, PROP_DATE, Format(Now, “yyyy-mm-dd hh:nn:ss”))
‘ 必要に応じてここで版数の自動インクリメントロジックを挿入可能
‘ 例: 特定のフラグが立っている場合のみリビジョンを上がる処理など
GoTo CleanUp
ErrorHandler:
Debug.Print “BeforeSave Error: ” & Err.Description
CleanUp:
Set props = Nothing
End Sub
‘ ——————————————————————————
‘ 内部ヘルパー関数群(カプセル化と堅牢性の担保)
‘ ——————————————————————————
Private Sub EnsureProperty(ByRef props As Visio.CustomProperties, ByVal propName As String, ByVal defaultValue As String, ByVal propType As Integer, ByVal label As String)
Dim i As Long
Dim exists As Boolean
exists = False
For i = 1 To props.Count
If props(i).Name = propName Then
exists = True
Exit For
End If
Next i
If Not exists Then
Dim prop As Visio.CustomProperty
Set prop = props.Add(propName)
prop.Value = defaultValue
prop.Label = label
prop.Type = propType
Set prop = Nothing
End If
End Sub
Private Sub SetPropertyValue(ByRef props As Visio.CustomProperties, ByVal propName As String, ByVal val As String)
On Error Resume Next
Dim prop As Visio.CustomProperty
Set prop = props.Item(propName)
If Not prop Is Nothing Then
prop.Value = val
End If
Set prop = Nothing
On Error GoTo 0
End Sub
Private Function GetNetworkUserName() As String
‘ Windows APIまたは環境変数からの安全なユーザー名取得
Dim userName As String
userName = Environ(“USERNAME”)
If userName = “” Then userName = “Unknown_System”
GetNetworkUserName = userName
End Function
—
3. システム間連携(PLM/ERP/Git)への展開知見
この `CustomDocumentProperties` に格納されたメタデータは、単にVisioのプロパティ画面で閲覧するためのものではない。真の価値は 外部システム(C#製デスクトップアプリ、PythonによるCI/CDパイプライン、PLMシステム)からのヘッドレス読み込み にある。
COM Interopを用いた外部からのメタデータ抽出
Visioのアプリケーションインスタンスを非表示(`Visible = False`)で起動し、ドキュメントを開くことなく(あるいはバックグラウンドで開いて)メタデータをスクレイピングするC# (.NET 8) のスニペットを提示する。これにより、Gitリポジトリへのコミット時に図面の版数とメタデータを自動的にインデックス化し、社内ポータルへ同期するパイプラインが完成する。
using Visio = Microsoft.Office.Interop.Visio;
public class VisioMetadataInspector
{
public static Dictionary
{
var metadata = new Dictionary
Visio.Application app = null;
Visio.Document doc = null;
try
{
app = new Visio.Application();
app.Visible = false;
app.ScreenUpdating = false;
// 読み取り専用かつ他のプロセスをブロックしないモードでオープン
doc = app.Documents.OpenEx(filePath,
(short)Visio.VisOpenSaveArgs.visOpenRO |
(short)Visio.VisOpenSaveArgs.visOpenNoWorkspace);
foreach (Visio.CustomProperty prop in doc.CustomProperties)
{
metadata[prop.Name] = prop.Value?.ToString() ?? string.Empty;
}
}
catch (Exception ex)
{
throw new InvalidOperationException($”Visioメタデータの読み取りに失敗しました: {filePath}”, ex);
}
finally
{
if (doc != null) { doc.Close(); Marshal.ReleaseComObject(doc); }
if (app != null) { app.Quit(); Marshal.ReleaseComObject(app); }
}
return metadata;
}
}
—
4. チーフアーキテクトからの警句
レガシーなVBA開発において、コードの記述量自体は問題にならない。問題は 「誰がその状態を担保するのか」 というガバナンスの欠如だ。
図面の作成部署や版数を人間の手入力に頼っている組織は、システム部門の怠慢と言わざるを得ない。今回提示したコードをテンプレート(`.vstx`)に埋め込み、全社展開することで、図面のトレーサビリティは完全に自動化される。
オブジェクトの解放を怠らず、イベントライフサイクルを正しく調停し、メタデータを「データ」として扱え。それこそが、VBAを単なる「マクロのおもちゃ」から「堅牢なエンタープライズ・エンジニアリング基盤」へと昇華させる唯一の道である。
