Visio VBAを制する:堅牢なエラーハンドリングとメモリ管理の深淵
Visioの自動化において、「コードが途中で止まる」ことは単なる不具合ではない。それは、複雑に絡み合ったシェイプ階層の整合性を破壊する「システムへの冒涜」である。
ドキュメント、ページ、シェイプ。この階層構造を辿る際、存在しないオブジェクトへのアクセスや、ロックされたセルへの書き込みは日常茶飯事だ。本稿では、単なる `On Error Resume Next` の乱用から脱却し、シニアエンジニアとして備えるべき「防御的プログラミング」の真髄を伝授する。
—
1. 「On Error Resume Next」の禁忌と、正しいガード節
多くの初学者は、エラーを隠蔽するために `On Error Resume Next` を多用する。これは地雷原を裸足で歩くようなものだ。真のアーキテクトは、「エラーを予測し、制御可能な範囲で握りつぶす」。
VisioのAPIは、特定の操作で例外を投げることを仕様としている。例えば、`Cell.Formula` へのアクセスで、そのセルがガード(Guard関数)されている場合、システムは容赦なく停止する。
推奨される堅牢なパターン
Public Sub SafeUpdateShapeText(shp As Visio.Shape, newText As String)
‘ エラーハンドラを局所化する
On Error GoTo HandleError
‘ セルがロックされているか事前にチェック
If shp.Cells(“LockTextEdit”).ResultIU <> 0 Then
Debug.Print “Warning: Shape ” & shp.Name & ” is locked.”
Exit Sub
End If
shp.Text = newText
Exit Sub
HandleError:
‘ ログ出力の責務を分離する
Logger.Log “Error in SafeUpdateShapeText: ” & Err.Description
Resume Next ‘ 処理を継続するか、あるいは安全に終了するかの判断をここで行う
End Sub
—
2. オブジェクトのライフサイクルと明示的解放の哲学
Visio VBAにおいて、オブジェクト変数を `Nothing` に明示的に設定しないことは、メモリリークを招く怠慢である。特にループ内で `Visio.Shape` や `Visio.Cell` を生成・取得し続ける場合、参照カウントが適切に減少しないリスクがある。
メモリ最適化の極意
Sub OptimizeShapeProcessing(pageObj As Visio.Page)
Dim shp As Visio.Shape
‘ ページ内の全シェイプを走査
For Each shp In pageObj.Shapes
‘ 処理…
Next shp
‘ 参照の明示的解放
‘ ループ変数であっても、明示的にNothingを代入する習慣が
‘ COMオブジェクトの解放遅延を防ぐ
Set shp = Nothing
End Sub
※ 大規模な図面を扱う際、この「解放」を怠るだけで、数千個のシェイプを処理する間にプロセスが肥大化する。
—
3. Windows APIとの連携:VBAの限界を突破する
Visioの標準APIだけでは、「現在のプロセスがバックグラウンドにあるか」「特定のウィンドウハンドルが有効か」といったOSレベルの制御が困難な場合がある。`User32.dll` を呼び出し、システムと直接対話することで、マクロの挙動をOSレベルで制御可能にする。
If VBA7 Then
Private Declare PtrSafe Function GetForegroundWindow Lib “user32” () As LongPtr
Else
Private Declare Function GetForegroundWindow Lib “user32” () As Long
End If
‘ マクロ実行中にVisioがアクティブかを確認し、予期せぬ入力を防ぐ
Public Function IsVisioForeground() As Boolean
If GetForegroundWindow() = Application.WindowHandle32 Then
IsVisioForeground = True
End If
End Function
このコードをガード節に組み込むことで、ユーザーが誤って別アプリを操作中にマクロが暴走し、誤ったシェイプを改変する事態を防げる。
—
4. 伝説のアーキテクトからの提言
システム開発において、エラーハンドリングは「付け足し」ではない。それはシステムの「設計思想そのもの」である。
1. 静的チェックを優先せよ: `Err` オブジェクトに頼る前に、`Shape.Type` や `Cell.Exists` で存在を確認する。
2. ログを構造化せよ: 実行時エラーが発生した際、どのページ、どのシェイプIDで発生したのかを即座に特定できるログ構造を構築せよ。
3. レガシーを恐れるな: 過去の汚いコードに手を加える際、最初に行うべきは「エラーハンドラの追加」ではなく「小さな関数単位への分解」である。
Visioという、描画とデータが混在する特異なプラットフォームにおいて、コードは「静的であると同時に動的」であるべきだ。貴殿が書くコードが、数年後のメンテナンスエンジニアにとって「迷宮」ではなく「地図」となることを切に願う。
―― 次回は、「Visioのイベントハンドラを駆使した、リアルタイム・プロパティ同期エンジンの構築」について掘り下げるとしよう。準備はいいか。
