【テクニカル・上級編】図面の変更検知:DocumentEventとApplicationEventを用いたリアルタイム自動処理の実装 – Visio VBA解析バイブル

スポンサーリンク

Visio VBAを掌握せよ:DocumentEventとApplicationEventによる「リアルタイム自動化」の深淵

Visioの自動化において、多くのエンジニアが陥る罠がある。それは「タイマー監視」や「ボタンを押して同期」という、原始的なポーリング手法だ。真のシニアアーキテクトであれば、イベント駆動(Event-Driven)の設計こそが、システムの堅牢性とUXを決定づける唯一の解であることを知っているはずだ。

本稿では、Visioのオブジェクトモデルを深く穿ち、図面の変更を検知してリアルタイムに追従する「常駐型エンジン」の構築論を説く。

1. オブジェクトのライフサイクル:イベントを「捕まえる」ための絶対条件

Visioでイベントを検知するには、単に `Document_DocumentOpened` を書くだけでは不十分だ。重要なのは、「いつイベントリスナーが生存し、いつ破棄されるか」というメモリ管理である。

多くの開発者が、クラスモジュールのインスタンス化を怠り、GC(ガベージコレクション)にオブジェクトを食わせることでイベントを喪失させている。

クラスモジュールによるイベントハンドラの設計(`clsVisioEvents`)

イベントを捕捉するには、`WithEvents` キーワードを用いてアプリケーションまたはドキュメントをラップする必要がある。

‘ クラスモジュール: clsVisioEvents
Option Explicit

‘ Applicationオブジェクトを監視対象として宣言
Public WithEvents vApp As Visio.Application

‘ 図形追加イベントのインターフェース
Private Sub vApp_ShapeAdded(ByVal Shape As IVisio.Shape)
‘ パフォーマンス最適化のため、必要最小限の処理のみを実行する
‘ ここで重い処理を行うとUIがフリーズするため注意
Debug.Print “Shape Added: ” & Shape.Name
Call AutoProcessShape(Shape)
End Sub

Private Sub AutoProcessShape(shp As Visio.Shape)
‘ ユーザーの操作に対するリアルタイム処理
‘ 例:特定のシェイプデータが含まれていれば自動でスタイルを適用する
If shp.CellExists(“Prop.ID”, 0) Then
shp.Cells(“FillForegnd”).Formula = “RGB(255,0,0)”
End If
End Sub

2. 永続性の確保:メインモジュールによる管理

イベントリスナーを保持するクラスのインスタンスは、`Public` な変数として標準モジュールで保持し、かつ決して `Nothing` にしてはならない。

‘ 標準モジュール: modMain
Option Explicit

Public g_EventListener As clsVisioEvents

Public Sub InitializeMonitor()
‘ 既にインスタンスが存在する場合は二重作成を防ぐ
If g_EventListener Is Nothing Then
Set g_EventListener = New clsVisioEvents
‘ Visioアプリケーション本体にフック
Set g_EventListener.vApp = Visio.Application
End If
End Sub

Public Sub TerminateMonitor()
‘ 明示的なオブジェクト解放
Set g_EventListener.vApp = Nothing
Set g_EventListener = Nothing
End Sub

3. シニアエンジニアが意識すべき「3つの技術的落とし穴」

① イベントの連鎖と再帰呼び出し(死のループ)

`ShapeAdded` イベント内で図形の属性(プロパティ)を書き換えると、それがトリガーとなって再び `ShapeChanged` や `CellChanged` が発火し、スタックオーバーフローや無限ループに陥ることがある。

  • 解法: `vApp.DeferEvents = True` を適切に活用し、処理の前後で状態を制御せよ。

② Windows APIとの連携によるパフォーマンス向上

Visioのイベントハンドラはメインスレッドで動作する。複雑なシステム連携(REST APIへの通信など)を行うと、Visioのレスポンスが極端に低下する。

  • 極限の知見: 重い処理は、`DoEvents` を挟むか、あるいは別プロセス(VB.NETで作成したCOMアドインまたはバックグラウンドプロセス)に委譲せよ。`SendMessage` APIを用いて、Visioから他プロセスへ非同期でシグナルを送るのが最もクリーンだ。

③ レガシー環境でのメモリリーク

Visioのイベントモデルは、ドキュメントを閉じてもインスタンスが残り続けることがある。

  • 解法: `Document_BeforeDocumentClose` イベントを必ずハンドリングし、ドキュメントの閉鎖に合わせて `g_EventListener` の破棄を確実に行うこと。これを怠ると、ゾンビプロセスがメモリを食い荒らす原因となる。

4. 総括:アーキテクトの矜持

Visio VBAは「古い」と切り捨てるのは容易い。しかし、図面という複雑なリレーションシップをリアルタイムに制御するインターフェースとして、これほど強力な環境は他にない。

「イベントを制する者は、システムを制す」。

コードを記述する際は、常に「この処理が1万個のシェイプに対して実行されたらどうなるか?」を想像してほしい。オブジェクトの生成・破棄のコストを最小化し、イベントの伝播経路を最短にする。その積み重ねこそが、何年経っても保守可能な、伝説的なシステムを生み出すのだ。

次は、COMアドイン(VSTO)への移行を見据えた、より堅牢な設計について語るとしよう。諸君の健闘を祈る。

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