【実務・中級編】存在しないシェイプやプロパティを指定してしまった時の安全策:Visio VBAのエラーハンドリング完全版 – Visio VBA解析バイブル

スポンサーリンク

Visio VBAを「堅牢なプロダクト」へ昇華させる:幽霊シェイプと消えたプロパティを封じ込める例外処理の極意

Visio VBAの世界へようこそ。私は数多の巨大なシステム図面、プラント設計、ネットワークトポロジの自動生成ツールを構築してきたチーフアーキテクトだ。

君がもし、「`On Error Resume Next`を冒頭に書いておけば安心だ」などと考えているなら、今すぐその考えを捨ててほしい。それはプログラミングではなく、単なる「現実逃避」だ。Visioというアプリケーションは、ExcelやWordとは比較にならないほど「オブジェクトの動的変化」が激しい。ユーザーがマクロ実行中にシェイプを削除する、あるいは独自のシェイプデータを消去する……こうした事態は日常茶飯事だ。

本稿では、存在しないシェイプやプロパティへのアクセスによってツールをクラッシュさせないための、「防御的プログラミング」の真髄を伝授する。

1. なぜVisio VBAは「落ちる」のか?:ID参照の罠

Visio開発者が最初にはまる罠、それが「シェイプIDへの過信」だ。

Visioの各シェイプには一意の`ID`が割り振られる。しかし、このIDは絶対ではない。ユーザーによる削除、Undo/Redo、あるいはページのコピーによって、昨日まで存在した`ID: 101`は、今日はもう存在しない「幽霊」と化す。

失敗する典型例

‘ 危険なコード:ID 101が存在することを前提としている
Set vsoShape = vsoPage.Shapes.ItemFromID(101) ‘ ここで実行時エラー「無効な識別子です」が発生
vsoShape.Text = “Update”

プロの設計では、「オブジェクトが存在しない可能性」をデフォルトとして扱う。

2. 存在確認の鉄則:`CellExists` と `ItemFromID` の安全なハンドリング

プロパティ(シェイプデータやカスタムプロパティ)にアクセスする際、最も強力な武器は `CellExists` プロパティだ。多くの開発者が `On Error` で無理やり突破しようとするが、それは計算コストが高く、デバッグを困難にする。

シェイプデータ(Prop.XXX)への安全なアクセス

Visioのシェイプデータは、セル(Cell)として管理されている。存在しないセルを `Cells(“Prop.Status”)` のように指定すると即座にエラーになる。これを防ぐには以下のロジックが不可欠だ。

”’

”’ 指定したシェイプに特定のプロパティが存在するか確認し、安全に値を書き込む
”’

Public Sub SafeSetShapeData(ByRef vsoShape As Visio.Shape, ByVal propName As String, ByVal value As String)
‘ “Prop.” プレフィックスの補完
Dim fullPropName As String
fullPropName = IIf(Left(propName, 5) = “Prop.”, propName, “Prop.” & propName)

‘ visExistsAnywhere (1) を使用して存在確認
If vsoShape.CellExists(fullPropName, visExistsAnywhere) <> 0 Then
vsoShape.Cells(fullPropName).FormulaU = Chr(34) & value & Chr(34)
Else
‘ ここでログを出力するか、必要に応じて行を追加する設計にする
Debug.Print “Warning: ” & fullPropName & ” does not exist in Shape ID ” & vsoShape.ID
End If
End Sub

3. 実戦用:堅牢なシェイプ取得ラッパー

次に、IDからシェイプを取得する際の「決定版」とも言える関数を紹介しよう。この関数は、エラーを呼び出し元に波及させず、`Nothing` を返すことで後続の処理に判断を委ねる。

”’

”’ IDからシェイプを安全に取得する。存在しない場合はNothingを返す。
”’

Public Function GetShapeByIdSafe(ByRef vsoPage As Visio.Page, ByVal shapeID As Integer) As Visio.Shape
On Error Resume Next ‘ このスコープ内のみ一時的にエラーを無視
Dim targetShape As Visio.Shape
Set targetShape = vsoPage.Shapes.ItemFromID(shapeID)
On Error GoTo 0 ‘ エラーハンドリングを即座にリセット

Set GetShapeByIdSafe = targetShape
End Function

4. プロダクション・グレードの実装例:データベース連携の堅牢化

実務では、Excelやデータベースのリストを元にVisio上のシェイプを更新するツールが多い。ここでは、「リストにはあるが、図面上から消されたシェイプ」を検知しながら処理を継続する、保守性の高いコード例を示す。

Public Sub SyncDiagramWithDatabase()
Dim vsoPage As Visio.Page: Set vsoPage = ActivePage
Dim shapeList As Variant
‘ 仮のデータ:ID 10, 25, 99 (99は削除済みと想定)
shapeList = Array(10, 25, 99)

Dim i As Long
Dim targetID As Integer
Dim vsoShape As Visio.Shape

For i = LBound(shapeList) To UBound(shapeList)
targetID = shapeList(i)

‘ 1. 安全に取得
Set vsoShape = GetShapeByIdSafe(vsoPage, targetID)

‘ 2. 存在チェック(ガード節による早期リターン)
If vsoShape Is Nothing Then
Debug.Print “Error: Shape ID ” & targetID & ” was not found. Skipping…”
GoTo ContinueLoop
End If

‘ 3. 属性更新(ここでも安全策を講じる)
UpdateShapeStatus vsoShape, “完了”

ContinueLoop:
Next i
End Sub

Private Sub UpdateShapeStatus(ByRef shp As Visio.Shape, ByVal status As String)
‘ セルの存在確認を行いつつ更新
If shp.CellExists(“Prop.Status”, visExistsAnywhere) Then
shp.Cells(“Prop.Status”).FormulaU = Chr(34) & status & Chr(34)
Else
‘ セルがない場合は新規作成するか、エラーログを出す
‘ 開発者の意図により設計が分かれるポイント
Debug.Print “Shape ID ” & shp.ID & ” has no ‘Status’ property.”
End If
End Sub

5. アーキテクトからの助言:ファイル・DB連携の注意点

Visio VBAで堅牢なツールを作るなら、以下の3つの設計指針を心に刻んでほしい。

1. 「IDは揮発性」だと理解せよ
外部データベースと連携する場合、Visioの内部ID(ShapeID)だけをキーにするのは危険だ。可能な限り、シェイプデータの特定行に「独自のユニークキー(GUID等)」を持たせ、それを使って検索するロジックを組むべきだ。

2. `visExistsAnywhere` の活用
`CellExists` には、シェイプそのものにあるか、マスターシェイプにあるかを判定するフラグがある。`visExistsAnywhere` を使うことで、継承されたプロパティも安全にチェックできる。

3. ユーザー操作への耐性
マクロ実行中にユーザーが「Ctrl+Z(元に戻す)」を押したり、シェイプを消したりする可能性を考慮せよ。重要な処理の前後では `Application.IsUndoingOrRedoing` をチェックするか、あるいは処理全体を `Application.BeginUndoScope` で包み、一貫性を保証せよ。

結論

エラーハンドリングとは、単にエラーメッセージを隠すことではない。「想定外の事態を想定内のフローに引き戻すこと」である。

今回紹介した `CellExists` による事前検知と、`On Error Resume Next` を極小スコープに限定したラッパー関数の組み合わせは、Visio VBA開発における「黄金律」だ。これを徹底するだけで、君の作成するツールの信頼性は劇的に向上し、深夜に鳴り響く「マクロが動かない」という電話をゼロに近づけることができるだろう。

コードを書くのではない。堅牢な構造を設計せよ。

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