【実務・中級編】Shape.CellExistsUによる安全なプロパティ参照:実行時エラーを未然に防ぐセル存在チェック – Visio VBA解析バイブル

スポンサーリンク

Visio VBAを掌握する極限の知見:Shape.CellExistsUによる要塞化プログラミング

Visio VBAの開発現場において、最も愚かしいクラッシュの原因は何だと思うか?
それは、「存在しないShapeSheetのセルにアクセスしたことによる実行時エラー(エラー番号:-2147352567 / 自動化エラー)」だ。

外部からインポートした図面、他人が適当に作ったカスタムシェイプ、あるいはバージョン違いのステンシル。これらをバルク処理する自動化スクリプトを書くとき、あなたのコードは「すべてのシェイプが完璧に特定のセルのプロパティを持っている」というお花畑のような前提に立っていないか?

もしそうなら、今すぐその甘い設計を捨ててほしい。
実務の現場で動くプロフェッショナルなツールとは、予期せぬ入力や欠損データに対してびくともしない「要塞のような堅牢性」を備えていなければならないのだ。

今回は、Visioのオブジェクトモデルの深淵を知る私から、`Shape.CellExistsU`を用いた完全防御型のプロパティ参照テクニックと、現場で即座に使えるプロダクションコードを授けよう。

なぜ `On Error Resume Next` の乱用は悪手なのか?

アマチュアプログラマーブルースは、エラーを回避するためにこう書く。

‘ 【アンチパターン】絶対に真似してはならないコード
On Error Resume Next
Dim val As String
val = shp.Cells(“Prop.Cost”).ResultStr(visNoCast)
If Err.Number = 0 Then
‘ 処理…
End If
On Error GoTo 0

これは最悪の悪手だ。理由を挙げよう。

1. パフォーマンスの著しい低下: VBAの例外トラップ機構は重い。大量のシェイプをループさせるバッチ処理でこれをやると、処理速度が目に見えて低下する。
2. バグの隠蔽: 想定外のエラー(メモリ不足やオブジェクト参照の喪失など)まで握りつぶしてしまい、デバッグが極めて困難になる。
3. コードの意図の崩壊: 「プロパティが存在するかどうか」の判定にエラーハンドリングを使うのは、論理破綻している。

我々が求めるべきは、エラーが起きる前に「静的あるいは動的に存在を検知する」ことだ。そこで登場するのが `CellExistsU` メソッド である。

`CellExistsU` の真価と正しい使い方

`Shape.CellExistsU` は、指定したシェイプのShapeSheet内に、該当するセルが存在するかどうかをブール値(True / False)で返す。

Dim isExists As Integer
isExists = shp.CellExistsU(“Prop.Cost”, visExistsLocally)

ここで重要なのが、第2引数(要件フラグ)の指定だ。ここを理解していないエンジニアが非常に多い。

| 引数の定数 | 意味 | 実務での判断基準 |
| :— | :— | :— |
| `visExistsLocally` (0) | そのシェイプ単体にセルが定義されているか。マスター(防壁)からの継承は含まない。 | カスタムプロパティ固有の値が入っているか厳密に確認したい時。 |
| `visExistsAnywhere` (1) | マスターシェイプやスタイルからの継承を含め、そのセルが評価可能か。 | プロパティの値やシェイプデータ(CostやIDなど)を「読み取る」だけならこちらが安全。 |

通常、データの読み取りや存在確認が目的であれば `visExistsAnywhere` を指定し、ローカルで上書き定義されているかをチェックしたい場合のみ `visExistsLocally` を使う。この使い分けがコードの質を決定づける。

【実務プロダクションコード】安全なプロパティ値の取得と設定

それでは、実際の業務でそのまま使える堅牢な関数を提示しよう。
このコードは、指定したシェイプから安全にカスタムプロパティ(ShapeData)を取得し、存在しない場合はデフォルト値を返す、あるいは動的にプロパティを追加するモジュールだ。

Option Explicit


‘ 指定したシェイプから安全にカスタムプロパティの値を取得する
‘ @param shp 対象の Shape オブジェクト
‘ @param propName プロパティ名(例: “Prop.AssetID”)
‘ @param defaultValue セルが存在しない場合に返すフォールバック値
‘ @return 取得した文字列、またはデフォルト値

Public Function GetShapePropertySafe(ByVal shp As Visio.Shape, ByVal propName As String, ByVal defaultValue As String) As String

‘ ガード節: シェイプが無効な場合は即座にデフォルトを返す
If shp Is Nothing Then
GetShapePropertySafe = defaultValue
Exit Function
End If

On Error GoTo ErrorHandler

‘ CellExistsU を用いて、継承も含めてセルが存在するかチェック (visExistsAnywhere = 1)
If shp.CellExistsU(propName, 1) <> 0 Then
‘ 存在する場合は安全に値を取得
GetShapePropertySafe = shp.CellsU(propName).ResultStr(visNoCast)
Else
‘ 存在しない場合はデフォルト値をフォールバック
GetShapePropertySafe = defaultValue
End If

Exit Function

ErrorHandler:
‘ 予期せぬ例外に対するフォールバック(ログ出力等の拡張ポイント)
Debug.Print “Error in GetShapePropertySafe: ” & Err.Description
GetShapePropertySafe = defaultValue
End Function


‘ 安全にプロパティが存在するか確認し、なければ新規追加した上で値を設定する
‘ @param shp 対象の Shape オブジェクト
‘ @param propName プロパティ名(例: “Prop.Status”)
‘ @param propValue 設定する値

Public Sub SetShapePropertySafe(ByVal shp As Visio.Shape, ByVal propName As String, ByVal propValue As String)

If shp Is Nothing Then Exit Function

Dim cellPath As String
cellPath = propName & “.Value”

‘ ローカルにセルが存在しない場合(visExistsLocally = 0)
If shp.CellExistsU(cellPath, 0) = 0 Then
‘ プロパティセクションが存在しない可能性があるため、
‘ 安全に追加処理を行うか、あるいはアドオン側でハンドリングする
‘ ※ここでは簡易的に、存在しない場合は新規セルとして追加を試みる
On Error Resume Next
shp.AddNamedRow visSectionProp, Replace(propName, “Prop.”, “”), visTagDefault
On Error GoTo 0
End If
番地
‘ 値の設定
If shp.CellExistsU(cellPath, 1) <> 0 Then
shp.CellsU(cellPath).FormulaU = Chr(34) & propValue & Chr(34)
End If
End Sub

データベースや外部ファイル連携における設計思想

この `CellExistsU` による防衛的プログラミングは、データベース(DB)やExcel等の外部データとVisio図面を同期(双方向バッチ)させるシステムにおいて、真価を発揮する。

例えば、DB側から「全シェイプの `Prop.AssetID` をキーにしてステータスを更新する」という要件があったとする。
世の中の大半のスクリプトは、図面内に想定外の「ゴミシェイプ(例えば、装飾用のただの四角形や線)」が混ざっていた瞬間、容赦なくクラッシュする。

しかし、前述した `CellExistsU` を挟んだ設計であればどうか?

Dim shp As Visio.Shape
For Each shp in ActivePage.Shapes
‘ アセットIDのプロパティを持っているシェイプだけを処理対象とする
If shp.CellExistsU(“Prop.AssetID”, 1) <> 0 Then
Dim assetId As String
assetId = shp.CellsU(“Prop.AssetID”).ResultStr(visNoCast)

‘ DBへの問い合わせ・更新処理へ安全に渡す
Call UpdateDatabaseStatus(assetId, “Synced”)
End If
Next shp

このアプローチにより、図面側がどのようなカオスな状態であっても、プログラム側は一切の例外を出さずに「処理できるものだけをスマートに処理し、関係ないオブジェクトは華麗にスルーする」という、極めてエレガントな振る舞いを実現できる。

チーフアーキテクトからの提言

自動化スクリプトの品質は、「異常系をどれだけ美しく飼いならしているか」で決まる。
正常系のハッピーパスだけを書くのは素人の仕事だ。プロフェッショナルは、現場の汚れたデータ、不完全なシェイプ、予期せぬ仕様変更を見越し、あらかじめコードの要所に `CellExistsU` という名の防壁を築く。

あなたの書くVisio VBAコードを、明日から「止まらない要塞」へとアップデートしてほしい。
それこそが、業務自動化エンジニアとしての生存戦略なのだから。

タイトルとURLをコピーしました