こんにちは。Visio VBAの世界へようこそ。
普段、マクロの記録から一歩踏み出し、自分だけの自動化ツールを作ろうと奮闘しているあなたの姿、素晴らしいですね。
しかし、Visio VBAの道を進むと必ずぶつかる「壁」があります。昨日まで動いていたコードが、図面上のシェイプを一つ消しただけで「実行時エラー ’91’: オブジェクト変数または With ブロック変数が設定されていません」と冷たく突き放してくる……そんな経験はありませんか?
Visioは非常に多機能な分、シェイプやプロパティ(シェイプデータ)の「存在」が非常に流動的です。今日は、そんな「予期せぬエラー」を優雅に回避し、プロ級の堅牢なコードを書くためのエラーハンドリングの極意を伝授します。
ここをマスターすれば、あなたのマクロは「壊れにくい、信頼されるツール」へと進化しますよ。
—
なぜVisio VBAは「エラー」が起きやすいのか?
通常のExcel VBAなどと違い、Visio特有の理由がいくつかあります。
1. IDの揮発性: シェイプを削除して作り直すと、見た目は同じでも内部の「ID」は変わります。
2. 名前の重複と消失: シェイプ名(例: “Process.10″)で指定していても、ユーザーがUI上で名前を変えてしまうことがあります。
3. シェイプデータの欠落: 「このシェイプには必ず『価格』というデータ項目があるはず」と思い込んでコードを書くと、その項目(セル)を持っていないシェイプに触れた瞬間にエラーになります。
これらを「プログラムのミス」と捉えるのではなく、「図面は常に変化するもの」という前提で備えるのが、一流のエンジニアの考え方です。
—
1. シェイプの存在を確認してから触る「安全な取得法」
まずは、特定のシェイプを操作する際の安全策です。いきなり変数に代入するのではなく、「そもそもそのシェイプ、今このページにいる?」を確認する習慣をつけましょう。
悪い例(すぐ止まる)
‘ もしID: 50 のシェイプが削除されていたら、ここでマクロが強制終了します
Set vsoShape = ActivePage.Shapes.ItemFromID(50)
vsoShape.Text = “Hello”
プロの書き方(安全策)
Visioには「ItemExists」のような便利なメソッドが標準で用意されていないため、エラーを逆手に取るか、ループで探すのが定石です。
”’
”’
Function GetShapeSafely(pg As Visio.Page, id As Long) As Visio.Shape
On Error Resume Next ‘ 一時的にエラーを無視
Set GetShapeSafely = pg.Shapes.ItemFromID(id)
On Error GoTo 0 ‘ エラー処理を元に戻す
End Function
‘ — 使い方 —
Sub Main()
Dim vsoShape As Visio.Shape
Set vsoShape = GetShapeSafely(ActivePage, 50)
If Not vsoShape Is Nothing Then
vsoShape.Text = “見つかりました!”
Else
MsgBox “シェイプが見つかりません。削除された可能性があります。”, vbExclamation
End If
End Sub
—
2. シェイプデータ(プロパティ)へのアクセスを鉄壁にする
Visio開発で最もエラーが多いのが、ShapeSheet(シェイプシート)のセルへのアクセスです。「シェイプデータ」にアクセスしようとして、その行(Row)が存在しない場合にエラーが発生します。
セルの存在チェックをルーチン化する
特定のプロパティ(例:`Prop.Cost`)を読み書きする前に、そのセルが存在するかどうかを確認する「ラッパー関数」を作っておくと、コードが劇的にスッキリします。
”’
”’
Function GetShapeDataSafe(vsoShape As Visio.Shape, rowName As String) As String
‘ セルが存在するか確認 (VisExists は非常に強力なメソッドです)
‘ visExistsAnywhere = セルが存在するかどうかを判定
If vsoShape.CellExistsU(rowName, Visio.visExistsAnywhere) <> 0 Then
GetShapeDataSafe = vsoShape.CellsU(rowName).ResultStrU(Visio.visUnitCodes.visNoCast)
Else
GetShapeDataSafe = “” ‘ 存在しない場合はデフォルト値を返す
End If
End Function
Sub UpdatePrice()
Dim shp As Visio.Shape
Set shp = ActiveWindow.Selection(1) ‘ 選択中のシェイプ
‘ “Prop.Price” という項目がある時だけ処理する
Dim price As String
price = GetShapeDataSafe(shp, “Prop.Price”)
If price <> “” Then
Debug.Print “価格は ” & price & ” です。”
Else
Debug.Print “このシェイプには価格データがありません。”
End If
End Sub
—
3. マクロ全体を守る「最後の方舟」:グローバルエラーハンドラー
個別のチェックだけでなく、予期せぬ事態(メモリ不足やファイル破損など)に備えて、プロシージャ全体を包み込むエラーハンドリングを実装しましょう。
Sub MasterAutomation()
‘ エラーが起きたら「ErrorHandler」ラベルまでジャンプさせる
On Error GoTo ErrorHandler
Dim vsoApp As Visio.Application
Set vsoApp = Application
‘ — ここにメインの処理を書く —
Dim shp As Visio.Shape
Set shp = ActivePage.Shapes(1)
shp.Text = “処理中…”
‘ —————————-
‘ 正常終了した場合は、ここでマクロを抜ける
Exit Sub
ErrorHandler:
‘ エラーの内容をユーザーに優しく伝える
MsgBox “申し訳ありません、処理中にエラーが発生しました。” & vbCrLf & _
“エラー番号: ” & Err.Number & vbCrLf & _
“内容: ” & Err.Description, vbCritical, “システムエラー”
‘ 必要に応じて後片付け処理をここに書く
End Sub
—
究極の知見:オブジェクトの「死」を意識する
Visio VBAを掌握するアーキテクトとして、一つだけ魂に刻んでおいてほしいことがあります。それは、「VBAの変数が持っているオブジェクト(ShapeやPage)は、ユーザーの操作一つで簡単に消滅する」ということです。
マクロの実行中に、ユーザーが手動でシェイプを消したり、ページを切り替えたりする可能性はゼロではありません。
- 処理の直前で取得する: 変数に長く保持せず、使う直前に `Shapes.Item` などで取得し直す。
- Nothingチェック: `If Not obj Is Nothing Then` を口癖にする。
この2点を意識するだけで、あなたの書くコードの堅牢性は、そこら辺のリファレンスを写しただけのコードとは比較にならないほど高まります。
まとめ
1. ItemFromID は直接使わない: ラッパー関数でラップして、`Nothing` かどうかをチェックする。
2. CellExistsU を使い倒す: セルにアクセスする前に、その「家(行)」があるか確認する。
3. エラーハンドラーは「お守り」: 予期せぬ停止を防ぎ、ユーザーに安心感を与える。
ここまでの内容を理解できれば、Visio VBAの基礎はもう完璧です。あなたはもう、ただ「記録されたコードを動かす人」ではなく、「堅牢なシステムを設計できる開発者」への第一歩を踏み出しました。
自信を持って、次の複雑な自動化にチャレンジしてくださいね。応援しています!
