Visio VBAを掌握する極限の知見:Shape.CellExistsUによる防衛的プログラミングの極意
エンタープライズ環境において、Microsoft Visioは単なるお絵描きツールではない。プラントの配管図、複雑なネットワークトポロジ、あるいはITインフラの構成管理データベース(CMDB)のビジュアルフロントエンドとして機能する、極めて高度なメタデータ保持コンテナである。
我々シニアエンジニアが構築する自動化スクリプトは、数千、数万のシェイプを秒単位で走査し、プロパティを読み書きしなければならない。その現場において、最も排除すべき敵は何か? それは「実行時エラーによる予期せぬスクリプトの停止」である。
今回は、Visio VBAのオブジェクトモデルにおける最大の地雷原の一つである「存在しないシェイプデータ(旧カスタムプロパティ)セルへのアクセス」を完全に無効化し、堅牢なシステム間連携を実現するための設計パターン`Shape.CellExistsU`を用いた防衛的プログラミングの極意を解説する。
—
1. なぜ「直接アクセス」は悪なのか? オブジェクトモデルの暗部
Visioでシェイプのプロパティ(ShapeSheet)にアクセスする際、多くの初学者や古いコードは以下のような書き方をする。
‘ 【アンチパターン】絶対に書いてはならないコード
Dim propValue As String
propValue = shp.Cells(“Prop.Cost”).ResultStr(visNoUnits)
このコードの何が問題か?
もし対象のシェイプに `Prop.Cost` というセルが存在しない場合、VBAランタイムは エラー ‘-2147352567 (80020009)’: Automation Error を吐き出して容赦なくクラッシュする。
「エラーハンドラー(`On Error Resume Next`)を使えばいいじゃないか」と思ったそこのあなた。その設計思想こそが、レガシーシステムの寿命を縮める元凶だ。
`On Error Resume Next` の隠れたコスト
VBAのエラーハンドリング機構は軽量ではない。例外トラップのコンテキストスイッチはパフォーマンスを著しく低下させる。数万回のループの中でこれを乱用すれば、GC(ガベージコレクション)やCOMレイヤーへの無駄なオーバーヘッドが蓄積し、処理時間が何倍にも膨れ上がる。
さらに悪質なのは、「本当に予期すべきエラー(メモリ不足やドキュメントの破損など)まで握りつぶしてしまう」という点だ。
存在するかどうかが不確実なプロパティにアクセスするためには、エラーが発生する前に「存在を確証する」アプローチ、すなわち防衛的プログラミング(Defensive Programming)が不可欠となる。
—
2. CellExistsUの正しい理解とユニバーサル名(U)の呪縛
Visio VBAには、セルの存在を確認するためのメソッドとして `CellExists` と `CellExistsU` の2つが用意されている。
チーフアーキテクトとしての結論を述べる。ローカライズ依存の `CellExists` は一切使うな。常に `CellExistsU` を使え。
セル名におけるローカライズの罠
VisioのShapeSheetは、日本語版Windows環境ではセルの名前がローカライズされて評価される場合がある(例: `Width` が `幅` になるなど)。特に古いレガシー図面や、異なる言語圏で作られたステンシルが混在する環境では、このローカライズの差異が致命的なバグを生む。
末尾に `U`(Universal)がついたメソッドやプロパティは、言語に依存しない不変の英語名(ユニバーサル名)でShapeSheetを操作することを保証する。システム間連携やAPI連携を視野に入れるなら、`CellExistsU` の一択なのだ。
‘ 構文
‘ intExists = Shape.CellExistsU(LocaleNameName_or_UniversalName, langFlags)
第2引数の `langFlags` には、通常 `visExistsAnywhere` (0) を指定する。これにより、セルのローカル名、ユニバーサル名の両方をスキャンし、かつセルの継承関係(マスターシェイプからの継承か、ローカルでの上書きか)を問わずに存在を確認できる。
—
3. 【実践】堅牢性を極めたプロパティ読み書きラッパー関数
ここからは、実際のエンタープライズ開発で即座に使える、実用的なVBAモジュールコードを提示する。
メモリの最適化、オブジェクトの適切な解放、そして `CellExistsU` を組み合わせた究極のセーフアクセスパターンだ。
Option Explicit
‘ ==============================================================================
‘ モジュール名: MdlShapeGuard
‘ 概要: Visioシェイプのプロパティ安全操作ライブラリ
‘ ==============================================================================
/
- 指定したシェイプに特定のShapeSheetセルが存在するか安全に確認する
- @param shp 対象のShapeオブジェクト
- @param universalCellName セルのユニバーサル名(例: “Prop.AssetID”, “Width”)
- @return 存在する場合はTrue
/
Public Function IsCellValid(ByRef shp As Visio.Shape, ByVal universalCellName As String) As Boolean
‘ 防衛的ガード:オブジェクトの有効性チェック
If shp Is Nothing Then
IsCellValid = False
Exit Function
End If
‘ CellExistsUによる高速かつ安全な存在確認 (visExistsAnywhere = 0)
On Error GoTo ErrorHandler
If shp.CellExistsU(universalCellName, 0) <> 0 Then
IsCellValid = True
Else
IsCellValid = False
End If
Exit Function
ErrorHandler:
‘ 予期せぬCOM例外が発生した場合のフォールバック
IsCellValid = False
End Function
/
- 安全にシェイプデータを取得する(存在しない場合はデフォルト値を返す)
- @param shp 対象のShapeオブジェクト
- @param propName プロパティ名(例: “Prop.ServerIP”)
- @param defaultValue セルが存在しない場合の代替値
- @return 取得した文字列、またはデフォルト値
/
Public Function GetShapeDataSafe(ByRef shp As Visio.Shape, ByVal propName As String, ByVal defaultValue As String) As String
Dim targetCellName As String
targetCellName = “Prop.” & propName
‘ セルの存在確認を行ってからアクセス
If IsCellValid(shp, targetCellName) Then
GetShapeDataSafe = shp.CellsU(targetCellName).ResultStr(visNoUnits)
Else
GetShapeDataSafe = defaultValue
End If
End Function
/
- 安全にシェイプデータを設定する(セルが存在しない場合は新規作成しない、またはログ出力)
- @param shp 対象のShapeオブジェクト
- @param propName プロパティ名
- @param val 設定する値
- @return 成功時はTrue
/
Public Function SetShapeDataSafe(ByRef shp As Visio.Shape, ByVal propName As String, ByVal val As String) As Boolean
Dim targetCellName As String
targetCellName = “Prop.” & propName
On Error GoTo SafeError
If IsCellValid(shp, targetCellName) Then
shp.CellsU(targetCellName).FormulaU = Chr(34) & val & Chr(34)
SetShapeDataSafe = True
Else
‘ セルが存在しない場合のビジネスロジック(必要に応じて動的追加も可だが、ここでは安全にスキップ)
SetShapeDataSafe = False
End If
Exit Function
SafeError:
SetShapeDataSafe = False
End Function
—
4. 大規模図面処理におけるメモリ最適化とパフォーマンスチューニング
数千のシェイプを持つ図面(P&IDや大規模ネットワーク図)を一括処理する場合、コードの書き方次第で処理時間が数分から数秒へと劇的に変わる。ここでシニアエンジニアが押さすべき極意を授ける。
1. 画面描画の凍結(ScreenUpdating)
VBAでシェイプのプロパティを書き換えるたびに、Visioは画面の再描画とShapeSheetの再計算を行おうとする。これを防ぐために、処理の開始前と終了後でアプリケーションの描画を明示的に停止させよ。
‘ 処理の高速化の常套手段
Visio.Application.ScreenUpdating = False
‘ — ここに重い処理 —
Visio.Application.ScreenUpdating = True
アプリ全体のイベントも一時停止(EnableCancelKey, EventEnabled)させるとさらに強固になる。
2. オブジェクトの明示的解放(メモリリーク対策)
VBAのCOMオブジェクトは自動解放されると過信してはならない。特に `Page.Shapes` コレクションやイテレーションループ内でのオブジェクト参照は、VBAの背後にあるCOM参照カウンタを圧迫し、メモリリーク(最悪の場合、Visioプロセスのクラッシュ)を引き起こす。
ループ内では必ずオブジェクト変数を初期化し、処理が終わったら `Nothing` を代入して明示的に解放せよ。
Public Sub BatchProcessShapes(ByRef targetPage As Visio.Page)
Dim shp As Visio.Shape
Dim i As Long
Dim count As Long
count = targetPage.Shapes.Count
‘ 逆順ループの推奨(シェイプの動的削除・追加がある場合)
For i = count To 1 Step -1
Set shp = targetPage.Shapes(i)
‘ CellExistsUを使った安全な処理
If IsCellValid(shp, “Prop.TargetFlag”) Then
‘ 処理本体
Call ProcessSpecificShape(shp)
End If
‘ COMオブジェクトの参照解放
Set shp = Nothing
Next i
End Sub
—
5. システム間連携(外部DB / API)への応用
この `CellExistsU` を軸とした防衛的プログラミングが真価を発揮するのは、外部データベース(SQL Server等)やREST APIとの同期処理(CMDB連携など)の現場である。
外部システムからJSONやCSV形式で送られてきたメタデータ群をVisio図面に流し込む際、図面側のシェイプが必ずしも最新のプロパティスキーマを持っているとは限らない。古いバージョンのテンプレートから作成された図面が混ざっていることは、現場では日常茶飯事だ。
‘ 外部データ同期のイメージ
Sub SyncShapeWithExternalDB(ByRef shp As Visio.Shape, ByVal dbData As Scripting.Dictionary)
Dim key As Variant
For Each key In dbData.Keys
Dim cellName As String
cellName = “Prop.” & CStr(key)
‘ セルが存在しない図面側シェイプであっても、エラーで止まらずにログを残す、
‘ あるいは動的にプロピングを追加するなどのハンドリングが可能になる。
If IsCellValid(shp, cellName) Then
shp.CellsU(cellName).FormulaU = Chr(34) & dbData(key) & Chr(34)
Else
‘ スキーマ不一致の検知とアラート(防衛的設計)
Debug.Print “Warning: Schema mismatch. Shape lacks cell: ” & cellName
End If
Next key
End Sub
このアプローチにより、データ構造の変更や古い図面の混入によるシステム全体の停止(ダウンタイム)を完全に防ぐことができる。
—
総括
Visio VBAの開発において、「動けばいいや」というコードは、数ヶ月後に必ずシステム運用の現場に牙をむく。
`Shape.CellExistsU` は、単なるエラー回避メソッドではない。それは、予測不可能なレガシー環境や大規模データの中でも、システムが自律的に身を守るための「防衛的アーキテクチャ」の基盤である。
オブジェクトのライフサイクルをコントロールし、細部にまで気を配った堅牢なコードを書くこと――それこそが、真のプロフェッショナルエンジニアリングである。
