【実務・中級編】Applicationレベルのタイマー(OnTime)実装:Visio内でバックグラウンド自動保存や定期データ同期を行う監視マクロ – Visio VBA解析バイブル

スポンサーリンク

Visio VBAを掌握せよ:Applicationレベルの「OnTimeタイマー」で実現する堅牢なバックグラウンド処理

業務自動化を志すエンジニアにとって、Visioというツールは「描画ソフト」ではなく「構造化データのインターフェース」です。しかし、Visio VBAには標準で「タイマーイベント」が存在しません。Excel VBAでお馴染みの `Application.OnTime` も、Visioには実装されていないのです。

多くのエンジニアがここで挫折し、無限ループの `DoEvents` を回してVisioをフリーズさせるか、諦めて手動実行のボタンを配置します。

今日は、Visioのオブジェクトモデルを深く理解した上で、「なぜVisioで非同期処理が困難なのか」を解き明かし、プロダクション環境で耐えうる「堅牢なタイマー実装」を伝授します。

1. なぜ「単純なループ」ではいけないのか

Visioはシングルスレッドで動作するGUIアプリケーションです。不適切なループ処理や、終了条件のないタイマーは、Visioのメインスレッドを占有し、UIを無反応(ハングアップ)させます。

私たちが目指すべきは、「Visioのメインサイクルを阻害せず、Windows APIを賢く利用して制御権を適宜委譲する」設計です。これには、Windowsのメッセージループを利用した `SetTimer` APIが最適です。

2. アーキテクチャの核心:SetTimerの実装

Visio VBAで周期処理を行うには、Windows APIの `SetTimer` を利用し、指定した時間が経過した際にコールバック関数(または特定のイベント)を発生させるアプローチをとります。

実装のポイント

  • モジュール分離: タイマー管理ロジックと、業務ロジック(同期・保存)を明確に分けること。
  • ライフサイクル管理: ドキュメントを閉じる際にタイマーを確実にKillする。これを怠ると、ゾンビプロセスが残り、Visioがクラッシュし続けます。

3. プロダクション環境向け実装コード

以下のコードを、標準モジュール(例: `TimerManager`)に貼り付けてください。

‘ タイマー管理モジュール
Option Explicit

If VBA7 Then
Private Declare PtrSafe Function SetTimer Lib “user32” (ByVal hwnd As LongPtr, ByVal nIDEvent As LongPtr, ByVal uElapse As Long, ByVal lpTimerFunc As LongPtr) As LongPtr
Private Declare PtrSafe Function KillTimer Lib “user32” (ByVal hwnd As LongPtr, ByVal nIDEvent As LongPtr) As Long
Private Declare PtrSafe Function GetModuleHandle Lib “kernel32” Alias “GetModuleHandleW” (ByVal lpModuleName As LongPtr) As LongPtr
Else
Private Declare Function SetTimer Lib “user32” (ByVal hwnd As Long, ByVal nIDEvent As Long, ByVal uElapse As Long, ByVal lpTimerFunc As Long) As Long
Private Declare Function KillTimer Lib “user32” (ByVal hwnd As Long, ByVal nIDEvent As Long) As Long
End If

Private m_TimerID As LongPtr

‘ タイマーを開始する
Public Sub StartBackupTimer(intervalMs As Long)
If m_TimerID <> 0 Then StopBackupTimer
‘ AddressOfは標準モジュール内のプロシージャを指定する必要がある
m_TimerID = SetTimer(0, 0, intervalMs, AddressOf TimerCallback)
End Sub

‘ タイマーを停止する(必須)
Public Sub StopBackupTimer()
If m_TimerID <> 0 Then
KillTimer 0, m_TimerID
m_TimerID = 0
End If
End Sub

‘ 周期的に実行される処理
Public Sub TimerCallback()
On Error Resume Next ‘ ここで落ちるとVisio全体が落ちるため最低限のガードを

‘ 業務ロジック:自動保存やデータ同期をここに記述
Debug.Print “Auto-sync triggered at: ” & Now

‘ 例: ActiveDocument.Save
‘ 例: API連携によるデータ同期処理
End Sub

4. 開発者が守るべき「3つの鉄則」

このコードを現場で運用する際、以下の3点を必ず徹底してください。

1. エラーハンドリングの徹底:
`TimerCallback` 内でエラーが発生すると、Visioのスタックが崩壊し、即座にアプリケーションが強制終了します。`On Error` を適切に使い、ログを出力する仕組みを必ず構築してください。
2. 終了処理のフック:
`ThisDocument` モジュールの `Document_BeforeClose` イベントで `StopBackupTimer` を呼び出すこと。これを行わないと、ドキュメントを閉じた後もタイマーが動き続け、存在しないオブジェクトを操作しようとしてエラーを吐き続けます。
3. 重い処理は外部へ:
タイマー内で巨大なデータベースへの同期や、複雑なXMLのパースを行うのは厳禁です。処理が長引くとUIのレスポンスが悪化します。同期処理はあくまで「フラグを立てる」か「非同期の外部ツール(exe)を起動する」程度に留めるのが、プロの設計です。

最後に:自動化の真髄

Visio VBAにおける自動化とは、単にマクロを動かすことではありません。「Visioという閉じた世界を、外部環境(サーバーやファイルシステム)と調和させる」ことです。

今回紹介した `SetTimer` によるアプローチは、非常に強力ですが、扱い方を間違えれば諸刃の剣となります。まずはローカルのファイル保存処理から導入し、安定稼働を確認してから、API連携等の高度な処理へ拡張してください。

コードのコピペで終わらせず、なぜその設計が必要なのかを考える。それが、あなたを「コードを動かす人」から「システムを設計できるエンジニア」へと変えるはずです。何か不明点があれば、遠慮なく議論しましょう。

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