Visio VBAの深淵:`CellExistsU`による「実行時エラー」の完全封殺と堅牢なアーキテクチャ
Visio VBAを用いた大規模な図面自動化において、最も忌むべき存在は何か。それは、異なるステンシルや不特定多数のユーザーが編集した図形が混在する環境で突如発生する「実行時エラー: 2043 (その名前のセルは存在しません)」である。
多くの駆け出しエンジニアは、エラーハンドリング(`On Error Resume Next`)でこの事象を強引に「握り潰す」という愚を犯す。これはメモリリークや未定義状態のオブジェクトを放置する、プロフェッショナルとしてあってはならない実装だ。
本稿では、`Shape.CellExistsU` を駆使し、Visioのオブジェクトモデルを完全に掌握するための極限の実装論を説く。
—
1. なぜ「握り潰し」がシステムを崩壊させるのか
Visioの `Shape.CellsU` プロパティは、存在しないセルを要求した瞬間に例外をスローする。`On Error Resume Next` をループ内で多用すれば、本来検知すべき他の重大なシステムエラーまで隠蔽され、デバッグ不能なスパゲッティコードが完成する。
真のアーキテクトは、「不確実性をコードの先頭で排除する」。存在確認は、プロパティアクセス以前の必須プロトコルであるべきだ。
—
2. 堅牢な実装:CellExistsUによる事前ガード
以下の関数は、私が現場で標準的に採用している「防衛的コーディング」のテンプレートである。`fExists` フラグを使い、無駄なエラーを発生させずに安全にアクセスを制御する。
”’
”’
Public Function GetShapeCellValue(shp As Visio.Shape, cellName As String) As Variant
‘ 存在チェック:第2引数(fExists)をTrueにすることで、エラーを発生させずに確認可能
‘ 0: 存在しない, 1: 存在する
If shp.CellExistsU(cellName, Visio.VisExistsFlags.visExistsAnywhere) <> 0 Then
GetShapeCellValue = shp.CellsU(cellName).ResultStr(“”)
Else
‘ 存在しない場合は空文字を返す、あるいは適切なデフォルト値を定義する
GetShapeCellValue = vbNullString
End If
End Function
この設計の肝
- `visExistsAnywhere` の指定: セルが図形レベルにあるか、マスターシェイプ側にあるかを問わず検索をかける。これにより、ステンシル由来の構造差異を吸収できる。
- 型安全性: `ResultStr` を用いることで、単位の差異を考慮しつつ文字列として正規化する。
—
3. メモリ解放とパフォーマンスの最適化
Visio VBAはCOMのラッパーである。`Selection` や `Shape` オブジェクトをループで回す際、暗黙的な参照がメモリを圧迫し、大規模図面ではVBAホストがクラッシュする。
オブジェクトライフサイクルの管理
Public Sub OptimizeDocumentTraversal(doc As Visio.Document)
Dim pg As Visio.Page
Dim shp As Visio.Shape
For Each pg In doc.Pages
For Each shp In pg.Shapes
‘ ここでセルアクセスを行う
If GetShapeCellValue(shp, “Prop.AssetID”) <> “” Then
‘ 処理ロジック
End If
Next shp
‘ ループごとに不要な参照をクリアする習慣を
Set shp = Nothing
Next pg
Set pg = Nothing
End Sub
—
4. 極限の視点:Windows APIとVisioの融合
もし、数万個の図形を高速にスキャンする必要があるならば、VBAのネイティブな `For Each` ループは遅すぎる。そんな時は、Visioの内部イベントをフックするか、場合によってはC++で作成したDLLを呼び出し、`GetCellsU` をバッチ処理で実行するアーキテクチャが求められる。
しかし、まずは「`CellExistsU` による事前チェック」という基本を完璧にこなすことだ。これができていないシステムは、どんなに高度なAPIを叩こうとも、例外処理の海で沈没する。
—
チーフアーキテクトからの提言
システム開発において、「エラーを回避する」ことは「エラーを処理する」ことよりも遥かに優れている。
- 型を信じるな: 外部から投入されるステンシルは「ゴミ」が入っている前提で設計せよ。
- 名前空間を意識せよ: `CellsU` (Universal) を使え。言語設定に依存する `Cells` (ローカライズ版) を使うのはアマチュアの所業だ。
- クリーンアップを徹底せよ: `Set obj = Nothing` は、ただの儀式ではない。COMの参照カウントを正しく管理し、Visioを安定させるための「生命維持装置」である。
君たちが記述するコードが、5年後のメンテナンス担当者から「なぜこんなに安定しているのか」と驚かれるような、堅牢なアーキテクチャであることを期待する。
技術は裏切らない。ただ、設計の甘さが技術を裏切るだけだ。
