Visio VBAの「沈黙」を制御せよ:ScreenUpdating = False における堅牢な例外処理の極意
Visio VBAで数千のシェイプを操作する際、`Application.ScreenUpdating = False` を記述するのは定石だ。しかし、多くのジュニアエンジニアはこの「魔法の呪文」を唱えた後、復旧の術を忘れる。エラーでコードが停止すれば、Visioは画面を更新しないまま「ゾンビ状態」で放置される。ユーザーは画面が固まったと勘違いし、タスクマネージャでVisioを強制終了させる。
これはコードの瑕疵ではない。アーキテクチャの敗北だ。
今回は、大規模なVisio自動化システムにおいても絶対に画面をフリーズさせない、堅牢なTry-Catch-Finallyパターンの実装を伝授する。
—
1. なぜ「単純なエラーハンドラ」では不十分なのか
VBAには標準で `Try…Catch` 構文がない。多くの開発者は `On Error GoTo ErrorHandler` を使うが、複雑なネストや再帰呼び出しを含むVisioのイベント処理において、スタックの巻き戻し中に描画フラグの再有効化を忘れるケースが後を絶たない。
特に、`Application` オブジェクトのプロパティは、Visioの描画スレッドと密接に結びついている。エラー発生時にスタックを正しくクリアし、必ず `ScreenUpdating = True` を通る「唯一の出口」を保証しなければならない。
—
2. 堅牢な例外処理の実装テンプレート
以下のコードは、あらゆるVisio自動化の基盤となるべき「ゴールデンパターン」である。
‘ — Visio自動化のアーキテクチャ・テンプレート —
Public Sub ProcessVisioAutomation()
Dim isUpdating As Boolean
‘ 事前準備:現在の状態を保持
isUpdating = Application.ScreenUpdating
On Error GoTo CleanUp
‘ 描画の停止:パフォーマンスの極限を追求する
Application.ScreenUpdating = False
‘ — メインの業務ロジック —
‘ ここにシェイプ操作やドキュメント処理を記述する
Call PerformComplexOperations
‘ —————————-
CleanUp:
‘ エラーの有無にかかわらず、必ず実行される終了処理
‘ オブジェクトの解放と描画状態の復元を保証する
Application.ScreenUpdating = isUpdating
‘ エラーが発生していた場合はここで再送出する
If Err.Number <> 0 Then
Dim errDesc As String
errDesc = “Visio Automation Error: ” & Err.Description
‘ 必要に応じてWindows Event Logへの書き出し等のロギングを行う
Debug.Print errDesc
Err.Raise Err.Number, “ProcessVisioAutomation”, errDesc
End If
End Sub
このパターンの要諦
1. 状態の保存と復元: `isUpdating = Application.ScreenUpdating` とすることで、元の状態が何であったかを問わず、呼び出し元の設定を破壊しない。
2. CleanUpラベルの一元化: `Resume` を使用せず、`GoTo` で必ず `CleanUp` を通るフローにする。これにより、どの段階でエラーが起きても描画更新が保証される。
3. 例外の再送出: `Err.Raise` を利用し、上位モジュールにエラーを伝播させる。エラーを握りつぶすのはバグの温床となる。
—
3. シニアエンジニアのための最適化:メモリとリソースの管理
Visioのオブジェクトモデルは、COM(Component Object Model)のラッパーである。`Page.Shapes` をループで回す際、オブジェクト変数を適切に破棄しなければ、メモリリークとパフォーマンス低下が同時に発生する。
オブジェクトの明示的解放
VBAのガベージコレクションを待つ余裕はない。大規模処理では以下の作法を徹底せよ。
Dim shp As Visio.Shape
For Each shp In ActivePage.Shapes
‘ 処理内容
‘ ループ内での明示的解放
Set shp = Nothing
Next shp
Windows APIによる「強制再描画」の補完
稀に、`ScreenUpdating = True` を実行してもVisioが描画をサボることがある。その場合は、`User32.dll` を呼び出し、ウィンドウの再描画を強制的に依頼する。
If VBA7 Then
Private Declare PtrSafe Function InvalidateRect Lib “user32” (ByVal hwnd As LongPtr, ByVal lpRect As LongPtr, ByVal bErase As Long) As Long
End If
‘ 描画更新後に呼び出す
InvalidateRect Application.WindowHandle32, 0, 1
—
4. 終わりに:アーキテクトの矜持
Visio VBAはレガシーとされることが多いが、適切に設計された自動化ツールは、今なおエンタープライズの現場で圧倒的な生産性を叩き出す。
「画面がフリーズした」というクレームは、エンジニアの敗北宣言だ。コードの裏側で何が起きているか、COMオブジェクトがどのようにリソースを占有しているか。それらを深く理解し、予期せぬエラーという「死」をコードの一部として受け入れ、美しく復旧させる設計こそが、我々エンジニアが目指すべき地平である。
さあ、コードを書け。ただし、完璧な出口を準備してからだ。
