Access VBAで「擬似マルチスレッド」を操る:Timerイベントの極意
Access VBAはシングルスレッドです。重い処理を走らせれば、画面はフリーズし、ユーザーは「壊れた」と判断してタスクマネージャーからAccessを強制終了させるでしょう。
多くの初心者は、ここで「DoEvents」をループ内に乱用し、CPUを焼き尽くす無様なコードを書きます。しかし、プロフェッショナルは違います。Accessが持つ「TimerInterval」という強力なイベントドリブン・エンジンを、非同期処理のスケジューラーとして再定義するのです。
今回は、バックグラウンドでのステータス監視や、長時間のバッチ処理を「メインスレッドを殺さずに」実行する、堅牢なアーキテクチャを伝授します。
—
なぜ「DoEvents」ループではいけないのか
ループ内で`DoEvents`を呼ぶのは、処理を一時中断してOSに制御を戻す「おまじない」に過ぎません。しかし、これでは処理の進捗管理が難しく、何より「ユーザーがフォームを閉じた瞬間に発生する不正なメモリアクセス」を制御できません。
堅牢な設計の鉄則:
- イベント駆動を徹底する: 処理を「開始」「継続」「終了」のステートに分解し、Timerイベントをタスクランナーとして使う。
- 状態保持(State Management): どの処理がどこまで進んだかをフォームのモジュールレベル変数で保持する。
- エラーの封じ込め: タイマー内で発生したエラーがAccess全体を落とさないよう、厳密な例外処理を記述する。
—
実装:非同期処理コントローラーの設計
このコードは、フォーム上に配置したタイマーを「疑似的なタスクスイッチャー」として機能させるテンプレートです。
Option Compare Database
Option Explicit
‘ 処理状態を管理する列挙型
Private Enum TaskState
Idle = 0
Processing = 1
Finished = 2
End Enum
‘ モジュールレベル変数で状態を保持
Private m_TaskState As TaskState
Private m_BatchCounter As Long
‘ — 処理開始ボタン —
Private Sub cmdStart_Click()
‘ タイマーを有効化(100ミリ秒ごとにイベント発火)
Me.TimerInterval = 100
m_TaskState = Processing
m_BatchCounter = 0
Debug.Print “バックグラウンド処理を開始しました。”
End Sub
‘ — タイマーイベント:ここが非同期処理の心臓部 —
Private Sub Form_Timer()
‘ 状態がIdleなら何もしない
If m_TaskState = Idle Then Exit Sub
‘ 処理の安全性を確保するためのエラーハンドラ
On Error GoTo ErrorHandler
‘ 1回のタイマーサイクルで「小さな処理」だけを行う(スライス処理)
‘ これによりUIのレスポンスが維持される
m_BatchCounter = m_BatchCounter + 1
‘ 例:100回で完了する重い処理のシミュレーション
If m_BatchCounter <= 100 Then
' ここに実務的な処理(API叩く、DB更新など)を記述
Debug.Print "処理実行中: " & m_BatchCounter & "%"
Else
' 完了処理
Me.TimerInterval = 0 ' タイマー停止
m_TaskState = Finished
MsgBox "バックグラウンド処理が完了しました。", vbInformation
End If
Exit Sub
ErrorHandler:
' 異常終了時は必ずタイマーを止めて暴走を防ぐ
Me.TimerInterval = 0
MsgBox "致命的なエラーが発生しました: " & Err.Description, vbCritical
End Sub
---
プロダクション環境で生き残るための「3つの規律」
このコードを現場に導入する際、以下の3点だけは必ず守ってください。
1. タイマー間隔(Interval)の最適化
100ms(10回/秒)は多くのケースで最適ですが、処理内容が重すぎる場合は500ms〜1000msまで広げてください。CPU使用率を上げすぎると、OS側から「応答なし」と判定されるリスクが増します。
2. DB接続の局所化(CurrentDbの扱い)
`CurrentDb`をタイマー内で何度も呼び出すと、オブジェクトの生成コストが積み重なります。必要であれば、モジュールレベルで`DAO.Database`オブジェクトを保持し、再利用する設計にしてください。ただし、長時間の保持はロックのリスクを伴うため、トランザクションの範囲は最小限に。
3. フォームのライフサイクルを考慮せよ
ユーザーが処理中にフォームを閉じると、タイマーは停止しますが、バックグラウンド処理が中途半端に残ることがあります。`Form_Unload`イベントにて、必ず`Me.TimerInterval = 0`を呼び出し、タスクを強制終了させるガードコードを書いてください。
—
最後に:エンジニアとしての矜持
VBAはレガシーと言われますが、その特性を理解し、OSのイベントキューを制御下に置くことができれば、驚くほど安定した業務システムを構築できます。
今回紹介した「タイマーによるタスク分割」は、単なる小手先のテクニックではなく、「シングルスレッド環境下でいかに並列性を擬似的に再現するか」という、アーキテクチャの本質に触れる手法です。
さあ、コードを書いて、Accessの限界を突破してください。あなたのツールが、誰かの業務時間を劇的に短縮することを期待しています。
