Access VBAで「擬似マルチスレッド」を制する:Timerイベントによるバックグラウンド処理の極意
Access VBAは、残念ながら「真のマルチスレッド」をサポートしていない。これはエンジニアにとっての呪縛のように思えるかもしれないが、実は、フォームのTimerイベントを「非同期タスクのスケジューラ」として昇華させることで、UIを一切ブロックせずにバックグラウンド処理を走らせることは可能だ。
多くの開発者が、重い処理をボタンクリックイベントに直書きし、Accessを「フリーズ」させるという失態を犯す。本稿では、その泥沼から抜け出し、堅牢かつスケーラブルなバックグラウンド処理を実装するためのアーキテクチャを伝授する。
—
1. なぜ「TimerInterval」が重要なのか
AccessのUIスレッドはシングルだ。つまり、VBAがコードを実行している間、Accessはユーザーのクリックや入力に応答できない。ここで`TimerInterval`の出番となる。
`TimerInterval`は一定時間ごとに`OnTimer`イベントを発生させる。これを「タスクの分割実行」のトリガーとして利用するのだ。長大な処理を一度に行わず、小さなチャンクに分割し、タイマーが鳴るたびに一つずつ消化する。これが、UIの反応性を維持しながらバックグラウンド処理を行う唯一の解である。
—
2. 堅牢なバックグラウンド処理の実装パターン
ただタイマーを動かすだけでは、エラーハンドリングや排他制御で詰む。以下の設計指針を遵守せよ。
1. 状態管理(State Machine): 今、何をしているのかをモジュールレベル変数で保持する。
2. 排他制御(Mutex的な発想): 処理中に次のタイマーイベントが発火しないよう、処理中は`TimerInterval = 0`にして停止させる。
3. 例外の封じ込め: タイマー内でエラーが起きてもAccessごと落ちないよう、厳格なエラーハンドリングを適用する。
実装コード:バックグラウンド・タスク・ランナー
このクラス(あるいはフォーム)を「バックグラウンドワーカー」として配置する。
Option Compare Database
Option Explicit
‘ 処理の進行状況を管理する列挙型
Private Enum TaskStatus
Idle = 0
Processing = 1
End Enum
Private m_Status As TaskStatus
‘ フォームロード時にタイマーを起動
Private Sub Form_Load()
Me.TimerInterval = 5000 ‘ 5秒ごとにチェック
m_Status = Idle
End Sub
‘ タイマーイベント:ここが「擬似スレッド」の心臓部
Private Sub Form_Timer()
‘ 二重起動防止:処理中はタイマーを止める
If m_Status = Processing Then Exit Sub
‘ 処理開始
Me.TimerInterval = 0
m_Status = Processing
Call PerformBackgroundWork
‘ 処理終了後、タイマーを再開
m_Status = Idle
Me.TimerInterval = 5000
End Sub
Private Sub PerformBackgroundWork()
On Error GoTo Err_Handler
‘ 【重要】ここは巨大な処理ではなく、
‘ 外部APIの叩き込みや、ログの同期など軽量なタスクを置く
Debug.Print “バックグラウンド同期開始: ” & Now
‘ データベースの更新チェックなど
‘ CurrentDb.Execute “UPDATE …”, dbFailOnError
Exit Sub
Err_Handler:
‘ タイマーイベント内でエラーを放置するとAccessが崩壊する。
‘ ログ出力に留め、アプリを落とさないこと。
Debug.Print “Error in Timer: ” & Err.Description
Resume Next
End Sub
—
3. ファイルやDB連携における「落とし穴」
このアーキテクチャを採用する際、避けて通れないのが「データベースのロック」と「ファイルアクセス権」だ。
- DAO/ADOの接続: タイマー内で毎回`CurrentDb`を生成するのは非効率だ。DB接続はモジュールレベルで保持し、再利用する設計にせよ。
- ファイルロック: 外部のExcelファイルやテキストファイルを監視する場合、ファイルが開かれているとVBAは容赦なくエラーを吐く。`Dir()`関数での存在確認や、エラーハンドラによるリトライ処理を必ず実装せよ。
- UIへのフィードバック: タイマーでバックグラウンド処理が完了した後、フォーム上のリストボックスやラベルを更新したい場合は、`Me.Requery`や`Me.Repaint`を忘れずに行うこと。
—
4. チーフアーキテクトからの助言
多くの開発者が陥る間違いは、「タイマーの中に重いループを入れること」だ。
タイマーイベントはあくまで「今の状態を確認し、必要なら小さな一歩を踏み出す」ためのもの。もし数分かかるような巨大なデータ処理が必要なら、Access VBAで完結させようとせず、PowerShellやCLIツールを呼び出し、結果だけをテーブルに書き込ませるのが、大人のアーキテクチャというものだ。
Access VBAは、使いようによっては非常に強力なツールになる。だが、「非同期処理=難しい」という先入観を捨て、イベント駆動の哲学を理解せよ。
あなたの書くコードが、ユーザーにとってストレスフリーな体験を生み出すことを期待している。健闘を祈る。
