Visio VBAの深淵:多言語・多バージョン環境を掌握する「環境依存排除」の極意
Visioという製品は、Officeスイートの中でも特に異端だ。そのオブジェクトモデルはシェイプシートという強力なデータベースを内包し、バージョンが変わるごとにUI言語や内部パスの解釈が微妙に変化する。
多くの開発者は、`Application.Version` を見て「もし2016なら…」と場当たり的な分岐を書く。だが、それは保守性の地獄への入り口だ。真のアーキテクトは、環境を特定するのではなく、「環境から抽象化されたインターフェース」を構築する。
本稿では、レガシー環境から最新の365までを横断し、一切の揺らぎを許さない堅牢なVisio VBA構築術を伝授する。
—
1. 「言語依存」という罠を克服する:Cell Nameの正体
Visioの最大の地獄は、`Shape.Cells(“Width”)` と記述した時に、ローカライズ版でエラーが発生することだ。日本語版Visioにおいて、一部のセル名は内部的に日本語化されているケースがある。
これを避ける唯一の解は、「Universal Name(不変名)」の使用である。
‘ 悪い例:言語環境に依存する
Set cell = shp.Cells(“高さ”) ‘ 日本語環境以外で崩壊する
‘ 良い例:Universal Nameを使用する
Set cell = shp.CellsU(“Height”) ‘ どの言語環境でも確実に動作する
すべてのプロパティ呼び出しにおいて `Cells` ではなく `CellsU` を、`Formula` ではなく `FormulaU` を使う。これはVisio開発における絶対原則である。
—
2. バージョン判定と環境抽象化レイヤーの構築
バージョンごとに動作を変える必要がある場合、グローバル変数で判定を繰り返すのは愚策だ。クラスモジュールを用いて「環境コンテキスト」を一度だけ生成せよ。
‘ クラス名: VisioContext
Option Explicit
Private m_Version As Double
Private m_IsJapanese As Boolean
Private Sub Class_Initialize()
‘ Versionを数値として保持(2016=16.0, 2019=16.0等)
m_Version = Val(Application.Version)
‘ 言語IDを取得して判定(1041 = 日本語)
m_IsJapanese = (Application.LanguageSettings.LanguageID(msoLanguageIDUI) = 1041)
End Sub
Public Property Get IsLegacy() As Boolean
‘ 2010以前をレガシーと定義する場合
IsLegacy = (m_Version < 14.0)
End Property
このように、環境情報は「値」として保持するのではなく「振る舞い」として隠蔽する。メインロジック側では `If Visio.Version > 16` などと書かず、`If Context.IsLegacy Then` と書くべきだ。
—
3. メモリ解放の極致:オブジェクトの生存期間を制御する
VisioのVBAは、COMオブジェクトの解放が非常にシビアだ。特に `Page` や `Shape` をループ内で生成・破棄する場合、明示的な解放を行わないとメモリリークが蓄積し、長時間のバッチ処理でVisioがクラッシュする。
Public Sub ProcessShapes(ByVal pg As Visio.Page)
Dim shp As Visio.Shape
Dim i As Long
‘ ループ内でのオブジェクト生成は最小限に
For i = 1 To pg.Shapes.Count
Set shp = pg.Shapes(i)
‘ 処理ロジック
Call OptimizeShape(shp)
‘ 参照の明示的解放
Set shp = Nothing
Next i
End Sub
また、`Application` や `Document` への参照をモジュールレベルで保持し続ける場合は、`Class_Terminate` イベントを必ず実装し、`Set m_App = Nothing` を明記すること。これが「伝説的」と呼ばれるシステムの安定性を支える作法である。
—
4. Windows APIによるパス取得の最適化
Visioはインストール先がレジストリの深い階層や、Click-to-Run環境で流動的になることがある。アドオンのパスなどを動的に解決する場合、`Environ` 関数に頼るのではなく、Windows APIを使用して確実にパスを追跡する。
If VBA7 Then
Private Declare PtrSafe Function GetSystemDirectory Lib “kernel32” Alias “GetSystemDirectoryA” (ByVal lpBuffer As String, ByVal uSize As Long) As Long
Else
Private Declare Function GetSystemDirectory Lib “kernel32” Alias “GetSystemDirectoryA” (ByVal lpBuffer As String, ByVal uSize As Long) As Long
End If
`#If VBA7` による条件付きコンパイルは、32bit/64bit Office環境が混在する現代の企業インフラにおいては標準装備だ。ここを疎かにするエンジニアに、大規模な配布ツールを作る資格はない。
—
結びに:なぜ「美しさ」を求めるのか
Visio VBAは、一見すると古い技術に見えるかもしれない。しかし、複雑な図形エンジンの背後で動作するこのコードは、企業の資産を守る「神経系」そのものである。
環境依存を排除し、メモリを管理し、抽象化された設計を貫く。このアプローチを徹底したシステムは、Visioのバージョンアップごとの改修コストをゼロに近づけることができる。
コードは「書く」ものではなく、「構築する」ものだ。明日、あなたの書く一行が、十年後の保守エンジニアを救うことを忘れないでほしい。
