【実務・中級編】イベントハンドラの無限ループ防衛:Application.EventsEnabled制御による再帰処理遮断 – Visio VBA解析バイブル

スポンサーリンク

Visio VBAの「死」を回避せよ:無限ループを遮断するイベント制御の鉄則

Visioの自動化において、イベント駆動プログラミングは諸刃の剣だ。`ShapeAdded`や`CellChanged`といったイベントをフックし、図形の配置や属性変更を自動化するのは強力だが、多くの開発者がここで躓く。

「図形を操作した瞬間に処理が再帰的に呼び出され、Visioがフリーズする(あるいはスタックオーバーフローで落ちる)」

これは、Visioのオブジェクトモデルを深く理解していない初心者が必ず通る「通過儀礼」である。しかし、プロフェッショナルな現場において、このバグは許されない。今回は、`Application.EventsEnabled`を駆使し、イベントの再帰発火を完璧に封じ込める堅牢なアーキテクチャを伝授する。

なぜ「無限ループ」は起きるのか?

Visioのイベントモデルは、あなたのコードによる「変更」をも「ユーザーによる変更」と区別しない。

1. `ShapeAdded` イベントが発火する。
2. イベントハンドラ内で、図形にメタデータを書き込む(`Cell.Formula`等を変更)。
3. その変更がトリガーとなり、再度 `ShapeAdded` または `CellChanged` が発火する。
4. 1に戻る。

これが「Visioが死ぬ」メカニズムだ。この連鎖を物理的に遮断する唯一の安全弁が `Application.EventsEnabled` プロパティである。

現場で使うべき「鉄壁」のイディオム

単にフラグをオフにするだけでは不十分だ。エラー発生時にフラグが「オフのまま」になると、その後のVisio操作が一切イベントを受け付けなくなるという、別の深刻なバグを生む。

必ず 「例外処理(Error Handling)で確実に復帰させる」 構造を維持すること。

実装例:堅牢なイベントハンドラ

‘ クラスモジュール (例: EventSink)
Private WithEvents AppEvents As Visio.Application

Private Sub Class_Initialize()
Set AppEvents = Visio.Application
End Sub

Private Sub AppEvents_ShapeAdded(ByVal Shape As IVShape)
‘ 1. イベント処理を一時停止
Dim oldEventsEnabled As Boolean
oldEventsEnabled = Visio.Application.EventsEnabled
Visio.Application.EventsEnabled = False

On Error GoTo Cleanup

‘ — ここからが実務処理 —
‘ 図形操作を行っても、再帰的なイベント発火は起きない
Shape.Cells(“Prop.Status”).Formula = “””Initialized”””
‘ — 実務処理ここまで —

Cleanup:
‘ 2. 異常終了時も必ずイベントを有効化して戻す
Visio.Application.EventsEnabled = oldEventsEnabled

‘ エラーが発生した場合はスタックトレースを残す等の処理
If Err.Number <> 0 Then
Debug.Print “Error in ShapeAdded: ” & Err.Description
End If
End Sub

アーキテクトからの助言:3つの設計原則

このコードをプロダクション環境に実装する際、以下の3点を意識してほしい。

1. 処理の最小化

`EventsEnabled = False` の範囲は、必要最低限のコードに限定せよ。このフラグをオフにしている間、Visioは他の重要なイベントもすべて無視する。処理が長すぎると、ユーザーの操作性が低下するだけでなく、外部データベース連携などの非同期処理との整合性が取れなくなる。

2. 状態管理の局所化

大規模なツールを作る場合、`EventsEnabled` をグローバル変数で管理するのは悪手だ。上記のようにクラスモジュールでイベントをカプセル化し、そのクラス内でフラグの状態を完結させること。これにより、複数のアドインやマクロが混在しても干渉を防げる。

3. データベース連携時の落とし穴

図形情報と外部DB(SQL Server等)を連携させる際、イベント内でDB更新を行うケースが多いだろう。もしDB接続がタイムアウトした場合、`EventsEnabled` が `False` のままコードが止まると、そのVisioファイルは「イベントを受け付けないゾンビ状態」になる。
DB通信は必ずタイムアウト制御を入れ、エラーハンドラを通るように設計すること。

まとめ:エンジニアの誇りとして

「とりあえず動く」コードを書くのは容易い。しかし、「異常系でも決して死なない」コードを書くことこそが、業務自動化エンジニアの付加価値だ。

Visioのイベントモデルは強力だが、その制御権を握っているのはあなた自身だ。`EventsEnabled` を適切にハンドリングし、再帰処理を支配下に置くこと。それができれば、あなたの作るツールは、チームにとって「信頼できる自動化エンジン」となるだろう。

次は、このイベントハンドラをいかにして「永続的なAdd-in」としてパッケージングし、保守性を高めるかについて議論したいところだが、それはまた別の機会にしよう。まずは今のコードの安全性を再確認してほしい。

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