Visio VBAの深淵:例外ハンドリングによる「不沈艦」アーキテクチャの構築
業務自動化の世界において、Visioは最も気難しい「貴婦人」だ。複雑なシェイプシート、不安定なオブジェクト参照、そして突如として発生するCOM例外。これらを放置して書かれたVBAコードは、単なる「動くスクリプト」であり、システムではない。
真のエンジニアは、エラーが起きないことを祈るのではなく、「エラーが起きた瞬間に、いかにしてシステムを安全に着水させるか」を設計する。本稿では、Visio VBAにおける例外ハンドリングの極致、その設計思想を伝授する。
—
1. なぜ「泥臭い」On Error GoTo が不可欠なのか
VBAの `On Error Resume Next` は、麻薬のようなものだ。安易に使えば、エラーを隠蔽し、メモリリークや不正なオブジェクト参照という「死の行進」を招く。
我々が求めるのは、「局所的なエラーを補足し、クリーンアップを行い、運用担当者が後から完全に追跡できるログを残す」という、極めて事務的かつ冷徹なエラーハンドリングである。
究極のエラーハンドラ設計図
すべてのプロシージャは、必ず以下の「定型」を守らなければならない。
Public Sub SafeShapeProcess(shpId As Long)
‘ エラーハンドラの設定
On Error GoTo ErrHandler
Dim shp As Visio.Shape
Set shp = ActivePage.Shapes.ItemFromID(shpId)
‘ ここにメインの業務ロジック
Debug.Print shp.Name
Cleanup:
‘ 【重要】リソースの解放は例外発生時も必ず通る経路にする
Set shp = Nothing
Exit Sub
ErrHandler:
‘ ログ記録専用のプロシージャへ投げる
Call LogError(Err.Number, Err.Description, “SafeShapeProcess”, shpId)
‘ 処理を安全に終了させるためのラベルへジャンプ
Resume Cleanup
End Sub
—
2. Windows APIを活用した高精度なログ出力
標準の `Debug.Print` や `MsgBox` だけでは、大規模な自動化環境では力不足だ。システム管理者が必要とするのは、発生時刻、マシン名、ユーザー名、そして詳細なスタックトレースである。
Windows APIの `GetComputerName` や `GetUserName` を組み合わせ、独自のログエンジンを構築する。
If VBA7 Then
Private Declare PtrSafe Function GetComputerName Lib “kernel32” Alias “GetComputerNameA” (ByVal lpBuffer As String, nSize As Long) As Long
Else
Private Declare Function GetComputerName Lib “kernel32” Alias “GetComputerNameA” (ByVal lpBuffer As String, nSize As Long) As Long
End If
Private Sub LogError(errNum As Long, errDesc As String, procName As String, context As String)
Dim logFile As Integer
logFile = FreeFile
‘ 追記モードでログ出力(共有ネットワークドライブ等での競合制御は別途実装が必要)
Open “C:\Logs\VisioAutomator.log” For Append As #logFile
Print #logFile, “[” & Now & “] PROC:” & procName & ” ERR:” & errNum & ” DESC:” & errDesc & ” ID:” & context
Close #logFile
End Sub
—
3. オブジェクトのライフサイクルとメモリの「呪い」
Visio VBAで最も恐ろしいのは、「ドキュメントを閉じてもプロセスがメモリから解放されない」現象だ。これはオブジェクトの参照がどこかに残っていることの証明である。
- 明示的なNothing化: `Set obj = Nothing` はおまじないではない。オブジェクトの参照カウントをデクリメントする唯一の手段だ。
- イベントハンドラの解除: `Document_BeforeDocumentClose` 等で、利用したクラスモジュールのインスタンスを確実に破棄せよ。
- Visio Applicationの保持: `ActiveDocument` ではなく、常に `Application.ActiveDocument` を明示的に参照することで、異なるインスタンス間での迷子を防ぐ。
—
4. シニアエンジニアへの提言:レガシーとの対峙
現場には、10年以上前に書かれた「ブラックボックス化されたマクロ」が溢れている。これらをリファクタリングする際、無理に全体を書き換えてはならない。
1. ラッパー関数で囲む: 既存の古いコードを触る前に、必ずラッパー関数を作り、その中で `On Error` を仕込む。
2. ログによる観測: まずはエラーを検知し、頻出するクラッシュポイントを特定する。
3. 部分的な近代化: 特定のモジュールから順に、上記のような堅牢な構造へと置き換えていく。
—
結びに:伝説のアーキテクトからの助言
Visio VBAはレガシーである。しかし、その強力な図形操作能力は、今なお他の言語では代替困難な価値を持っている。
エラーを恐れるな。エラーハンドリングこそが、あなたのコードを「ただのプログラム」から「信頼に足るシステム」へと昇華させる唯一の手段だ。エラーをログに刻み、それを解析し、コードを最適化し続ける。その泥臭い反復の先にしか、真の自動化の境地は存在しない。
今日もまた、ログファイルに静かなる記録を残そう。それが、システムが生きているという証なのだから。
