Access VBAを掌握する極限の知見:TimerIntervalが生む「擬似マルチスレッド」の魔力
こんにちは。開発プロジェクトの現場で、数々のモンスター級Accessシステムを立て直してきたチーフアーキテクトの私だ。
Accessと聞いて、「シングルスレッドだから重い処理を走らせると画面がフリーズする」「バックグラウンド処理なんて無理だ」と諦めていないか?
確かに、Access VBAはVBScriptやVB6の系譜を引くシングルスレッドランタイムであり、重たいループ処理(`Do While`など)や、巨大なADO/DAOのレコードセット操作をメインスレッドで直接実行すれば、容赦なく画面は「応答なし」になり、ユーザーはイライラを募らせる。
しかし、フォームの `TimerInterval` プロパティと `Timer` イベントの特性を深く理解し、状態を保持する「ステートマシン(状態機械)」の概念を持ち込めば、Accessの単一スレッド上で極めて堅牢な非同期風のバックグラウンド処理(擬似マルチスレッド)を実装できる。
今回は、実務の現場で「おっ、やるな」と唸らせる、タイマーイベントを駆使した非同期ステータス監視・ログ処理の極意を伝授しよう。
—
1. なぜ「同期処理のループ」は実務で破綻するのか
業務システムを作っていると、以下のような要求に直面する。
- 「外部APIやファイルサーバーのフォルダを常時監視し、新着ファイルがあれば自動取り込みしたい」
- 「重い一括処理の進捗率をリアルタイムにプログレスバーで表示しつつ、キャンセルも受け付けたい」
素人プログラマーがやりがちな悪手はこれだ。
‘ 【やってはいけないアンチパターン】
Sub HeavyProcess()
Do While Not IsFinished
‘ フォルダ監視や重い処理
Call CheckFiles
DoEvents ‘ ← これでごまかそうとする
Loop
End Sub
`DoEvents` を挟めば画面が固まるのを防げると思ったら大間違いだ。イベントキューに制御を戻す代償として、ユーザーの予期せぬダブルクリックやフォームの閉じるボタン(×)を拾ってしまい、多重起動やメモリリーク、最悪の場合はデータベースの破損(.accdbのコリジョン)を引き起こす。これが「VBAは使えない」と言われる所以だ。
非同期を実現する鍵は、「処理を細切れ(チャンク)に分割し、Accessのアイドル時間に少しずつ実行させること」にある。それを極めてエレガントに実現するのが `TimerInterval` だ。
—
2. TimerInterval設計の3大原則
フォームの `TimerInterval` にはミリ秒単位で数値を設定できる(例: `1000` なら1秒ごと)。これを安全に運用するための鉄則は以下の3つだ。
1. リエントラント(再入可能性)の排除
タイマーイベントの処理が終わる前に次のタイマー発火が来ると、同一プロシージャが二重実行され、デッドロックやオブジェクト競合の原因になる。必ず「実行中フラグ(`IsProcessing`)」でガードせよ。
2. エラーの完全捕捉とサイレント化の防止
バックグラウンドで予期せぬエラーが起きてアプリ全体が沈黙するのが最悪のパターン。エラー時はタイマーを即座に止め、ログに書き出して安全に復帰させろ。
3. CurrentDbの乱用禁止
タイマー内で毎回 `CurrentDb` を呼び出すと、オブジェクト生成・破棄のオーバーヘッドでパフォーマンスが劣化する。必要最小限のスコープに留めろ。
—
3. 【プロダクションコード】擬似マルチスレッド・ワーカーフォームの実装
ここでは、メイン画面を一切フリーズさせず、一定間隔(例: 3秒ごと)に非同期で「フォルダの監視」と「ログのフラッシュ」を行うバックグラウンドワーカーフォーム `frmBackgroundWorker` の実装コードを提示する。
実装手順
1. Accessで新しいフォームを作成し、名前を `frmBackgroundWorker` とする。
2. フォームのプロパティで以下を設定:
- `TimerInterval` = `3000` (3秒おきに発火)
- `Visible` = `False` (あるいはメイン画面の隅でアイコン化)
3. 以下のコードをフォームのモジュールに貼り付ける。
Option Compare Database
Option Explicit
‘ =========================================================================
‘ 模範的バックグラウンドワーカー実装
‘ =========================================================================
‘ 多重実行を防ぐための排他制御フラグ
Private m_IsProcessing As Boolean
‘ ステータス管理用カウンター(テスト用)
Private m_TickCount As Long
Private Sub Form_Load()
On Error GoTo ErrorHandler
m_IsProcessing = False
m_TickCount = 0
‘ 起動時に必要な初期化処理があればここに記述
Debug.Print “[” & Now & “] バックグラウンドワーカー起動: 待機中…”
Exit Sub
ErrorHandler:
MsgBox “ワーカーの初期化に失敗しました: ” & Err.Description, vbCritical
End Sub
Private Sub Form_Timer()
‘ ———————————————————————
‘ タイマーイベント(デフォルトで TimerInterval 間隔で発火)
‘ ———————————————————————
‘ 【原則1】すでに処理中であれば、今回のタイマー割り込みはスキップ(多重実行防止)
If m_IsProcessing Then Exit Sub
‘ 排他ロック取得
m_IsProcessing = True
On Error GoTo ErrorHandler
‘ カウントを進める
m_TickCount = m_TickCount + 1
‘ — ここにバックグラウンドで行いたい処理を記述 —
‘ 例: 共有フォルダの新着ファイルチェック、ログのバッファ書き込みなど
Call DoBackgroundTasks(m_TickCount)
‘ 処理完了、ロック解除
m_IsProcessing = False
Exit Sub
ErrorHandler:
‘ 【原則2】バックグラウンドエラーのハンドリング
m_IsProcessing = False
‘ 万が一のエラーでタイマーが暴走しないよう、必要ならタイマーを停止
‘ Me.TimerInterval = 0
Debug.Print “[” & Now & “] ⚠️ バックグラウンド処理エラー: ” & Err.Description
‘ 実務ではここでエラーログテーブルへの書き込みや、管理者のメール通知などを実装する
End Sub
Private Sub DoBackgroundTasks(ByVal tick As Long)
‘ 実際の重くない、あるいは細切れにされた非同期処理
Debug.Print “[” & Now & “] バックグラウンドタスク実行中… (Cycle: ” & tick & “)”
‘ 【実務的ヒント】
‘ ここでファイルシステムのチェックや、外部APIのステータス確認を行う。
‘ 例: CheckSharedFolderForNewFiles()
‘ ※注意: この中で長大なループ(Do While等)を回してはならない。
‘ あくまで「1回のタイマーイベント内で完了する単位」の処理に分割すること。
End Sub
Private Sub Form_Unload(Cancel As Integer)
‘ 終了時の安全なクリーンアップ
Me.TimerInterval = 0
Debug.Print “[” & Now & “] バックグラウンドワーカー正常終了.”
End Sub
—
4. この設計がもたらす圧倒的なメリット
このアーキテクチャを採用することで、現場の開発において以下の強烈なメリットが生まれる。
- UIスレッドの完全な保護
ユーザーがメインフォームでボタンを押したり、データ入力をしたりする操作感(レスポンス)が、バックグラウンド処理の存在によって損なわれることが一切ない。
- 安全なスケーラビリティ
複雑なマルチスレッドAPI(Windows APIのCreateThread等)をAccessで無理に使おうとすると、VBAのメモリ管理機構と衝突して容赦なくAccessごと強制終了(クラッシュ)する。`TimerInterval`によるイベント駆動型の細切れ処理であれば、Accessのランタイムの安全圏内で確実に動作する。
- 保守性の高さ
コードが「イベントドリブン」で綺麗にモジュール化されているため、後任のエンジニアが見てもどこで何をしているかが一目で分かる。
—
チーフアーキテクトからの総括
Accessは「おもちゃのデータベース」などと揶揄されることがあるが、それは使い手側の設計思想が未熟な場合の話だ。今回紹介した `TimerInterval` によるステート管理と非同期風アプローチをモノにすれば、Accessは十分に堅牢でモダンな業務自動化プラットフォームへと昇華する。
安易な `DoEvents` や同期ループに頼るコードとは今日で決別し、エレガントでプロフェッショナルな設計をあなたの現場に導入してほしい。健闘を祈る。
