Visio VBAの断末魔を回避せよ:異種混在環境を制する「バージョン適応型アーキテクチャ」の極意
Visioというソフトウェアは、Officeスイートの中でも異質な存在だ。数十年にわたるレガシーなCOMインターフェースと、最新のUWP/Webベースの潮流が混在するこの環境で、愚直にコードを書くことは死を意味する。
特に「Visio 2013, 2016, 2019, 2021, Microsoft 365」が社内で乱立する環境において、ハードコーディングされたロジックは、最初のアップデートで崩壊する。今回は、バージョン差異を「吸収」し、システムの堅牢性を担保するための、チーフアーキテクトとしての私の知見を共有する。
—
1. なぜ `Application.Version` だけで判断してはいけないのか
多くの初心者は `Application.Version` の戻り値(”15.0″, “16.0” 等)を数値比較して処理を分岐させる。だが、これには罠がある。Microsoft 365版のVisioは、マイナーアップデートによって内部挙動が微妙に変わるからだ。
真のプロは、バージョン番号に依存するのではなく、「機能の存在有無」をラッパー関数で抽象化する。
‘ バージョン判定をカプセル化したグローバル・セーフティ・モジュール
Public Function IsVisioVersionAtLeast(ByVal requiredMajor As Double) As Boolean
‘ Versionプロパティは文字列で返るため、安全にDoubleへ変換
Dim currentVersion As Double
On Error Resume Next
currentVersion = CDbl(Application.Version)
On Error GoTo 0
IsVisioVersionAtLeast = (currentVersion >= requiredMajor)
End Function
2. 言語識別とAPIの「境界」を越える
Visioの `Shape.Name` や `Master.Name` は、インストールされている言語(LCID)に依存して変化する。これが「多言語展開」を阻む最大の障壁だ。
ここで使うべきは `NameU`(Universal Name)である。UI上の表示名はローカライズされるが、`NameU` は常に英語ベースで固定されている。
教訓:ローカル名(Name)を扱うコードを書いた時点で、そのコードは「レガシーの負債」確定である。
‘ 互換性を保つための「NameU」参照パターン
Public Sub GetSafeShapeName(shp As Visio.Shape)
‘ 常にNameUを参照し、言語環境に左右されないロジックを組む
Debug.Print “Shape Name (Localized): ” & shp.Name
Debug.Print “Shape Name (Universal): ” & shp.NameU
End Sub
3. メモリ解放の作法:COMオブジェクトの「残滓」を殺せ
Visio VBAで最も多いエラーは「実行時エラー 462: リモートサーバーがないか、利用できません」である。これは、暗黙的に生成されたCOMオブジェクトがメモリ上に残り続け、ゾンビプロセスとなってVisioの終了を阻害している証拠だ。
以下の「Disposeパターン」を徹底せよ。
Public Sub ProcessShapesSafely()
Dim vsoDoc As Visio.Document
Dim vsoPage As Visio.Page
Dim vsoShp As Visio.Shape
Set vsoDoc = ActiveDocument
Set vsoPage = vsoDoc.Pages(1)
‘ ここでループ処理を行う
For Each vsoShp In vsoPage.Shapes
‘ 処理内容
Next vsoShp
‘ 重要:参照を明示的にクリアする
‘ Visioオブジェクトモデルにおいて、これを怠るとメモリリークの温床となる
Set vsoShp = Nothing
Set vsoPage = Nothing
Set vsoDoc = Nothing
End Sub
4. 極限の知見:Windows APIによる「ハングアップ回避」
Visioが大規模な図面を処理する際、画面更新がフリーズして「応答なし」と判定されることがある。これを防ぐには `Application.ScreenUpdating` をオフにするのが定石だが、それでも不安定な場合は、Windows APIでイベントループを強制的に制御する。
If VBA7 Then
Private Declare PtrSafe Sub Sleep Lib “kernel32” (ByVal dwMilliseconds As Long)
Else
Private Declare Sub Sleep Lib “kernel32” (ByVal dwMilliseconds As Long)
End If
Public Sub HeavyProcess()
Application.ScreenUpdating = False
‘ 重い処理の合間にDoEventsを挟むのは古典的だが、
‘ Sleepを適度に挟むことでCPUの独占を防ぐのが現代的なアーキテクチャだ
‘ [処理ロジック…]
Application.ScreenUpdating = True
End Sub
—
結びに代えて:エンジニアが持つべき「視点」
Visio VBAを触るということは、単に図を描くツールを作るのではない。「Visioというブラックボックスを、コードというメスで外科手術する」という意識を持つことだ。
1. バージョンを信用するな:機能の存在をプロパティでチェックせよ。
2. 名前を信用するな:`NameU` を使い、言語環境の揺らぎを排除せよ。
3. メモリを信用するな:`Set = Nothing` はおまじないではなく、必須の儀式である。
これらを守るだけで、あなたのコードは「その場しのぎのスクリプト」から、組織の資産となる「堅牢なエンジン」へと昇華するはずだ。次の現場で、この知見があなたの最大の武器になることを期待する。
