【Access VBAを掌握する極限の知見】Echoを支配せよ:エラーをも貫く画面描画制御の完全実装
長年、数百万レコードを抱える巨大なAccess基幹システムと対峙してきたシニアエンジニアであれば、誰もが一度は「画面のちらつき(Flicker)」という悪霊に悩まされたことがあるはずだ。
大量のレコードをループ処理で更新する際、VBAは律儀にフォームやクエリの再描画を繰り返す。結果としてタスクマネージャーのCPU使用率は跳ね上がり、画面はストロボのように明滅し、処理速度は劇的に低下する。
この非効率を断ち切るために用意されているのが `Application.Echo False` である。
しかし、実務において `Echo` を安易に使うことは、時限爆弾を抱えることに等しい。なぜなら、処理の途中で予期せぬ実行時エラー(データ型不一致、ロック競合、ヌル参照など)が発生し、エラーハンドラーへジャンプした瞬間、あるいはコードが途中で中断された瞬間、画面の描画が停止したままの「ゾンビ状態」が完成するからだ。
画面が凍りついたように一切更新されなくなったAccessを目の当たりにした一般ユーザーは、パニックに陥り、タスクを強制終了させる。そしてデータベースには中途半端なトランザクションの残骸が残される。
この地獄絵図を防ぐ唯一の解が、「オブジェクトのライフサイクルを利用した確実なEchoの自動復帰メカニズム」である。今回は、VBAにおけるRAII(Resource Acquisition Is Initialization)の概念を模した、エラーフリーな画面描画制御クラスの設計思想と実装のすべてを伝授する。
—
1. なぜ `On Error Resume Next` では不十分なのか?
アマチュアプログラマーは、エラー対策として以下のようなコードを書く。
Sub BadImplementation()
Application.Echo False
‘ 何らかの爆弾を抱えた処理
Dim rs As DAO.Recordset
Set rs = CurrentDb.OpenRecordset(“NonExistentTable”) ‘ここでエラー!
Application.Echo True
End Sub
このコードでエラーが発生した場合、`Application.Echo True` は永遠に実行されない。`On Error GoTo ErrorHandler` を書いても、開発者がハンドリングを少しでも誤れば、やはり画面は閉ざされたままになる。
我々が求めるべきは、「VBAの実行コンテキストが失われた瞬間(スコープから外れた瞬間)に、如何なる例外があろうとも確実に `Echo True` を強制発動させる仕組み」である。
ここで鍵となるのが、VBAクラスモジュールの `Class_Terminate` イベントである。
—
2. クラス設計:`clsScreenLock` の全貌
VBAのクラスインスタンスは、変数のスコープを抜けた(あるいはエラー等でプロシージャが強制終了した)際、ガベージコレクションによって確実に破棄され、`Terminate` イベントが走る。このライフサイクルを逆用するのだ。
以下のコードを、新規クラスモジュール `clsScreenLock` として実装してほしい。
‘ =========================================================================
‘ クラス名: clsScreenLock
‘ 概要: スコープ制御による確実なApplication.Echo復帰クラス
‘ =========================================================================
Option Explicit
Private m_IsLocked As Boolean
‘ クラス初期化時にEchoをオフにする
Private Sub Class_Initialize()
On Error GoTo ErrorHandler
‘ 既にネストされている場合の多重制御はあえてせず、シンプルにロック
Application.Echo False
m_IsLocked = True
Exit Sub
ErrorHandler:
‘ 万が一のEcho失敗時はフラグを落とす
m_IsLocked = False
End Sub
‘ クラス破棄(スコープアウト/エラー終了時)に必ずEchoをオンに戻す
Private Sub Class_Terminate()
‘ クラスが確実にロック状態を保持していた場合のみ復帰処理を実行
If m_IsLocked Then
On Error Resume Next
Application.Echo True
‘ メモリ上の安全性を考慮し、必要に応じたオブジェクト解放のトリガーをここに置くことも可能
On Error GoTo 0
End If
End Sub
‘ 【極限の知見】処理の健全性をアサートするための手動解除メソッド
Public Sub ReleaseNow()
If m_IsLocked Then
Application.Echo True
m_IsLocked = False
End If
End Sub
アーキテクチャの解説:なぜこの設計が最強なのか?
1. スコープバインドの原則:
このクラスのインスタンスをプロシージャ内のローカル変数として宣言する。プロシージャが終了する(Normal Endであれ、Unhandled Errorであれ)と、VBAランタイムは自動的にインスタンスの参照カウントをゼロにし、`Class_Terminate` を呼び出す。
2. `On Error Resume Next` のカプセル化:
万が一、`Application.Echo True` 自体が何らかのシステム競合でエラーを吐いたとしても、`Terminate` 内の `On Error Resume Next` がプロセス全体のクラッシュを防ぐ。
3. 明示的な早期解放 (`ReleaseNow`):
長大なプロシージャの後半で、ユーザーに処理結果のメッセージボックスを表示させたい場合など、スコープを抜ける前に描画を戻したい場面に対応する。
—
3. 実践:現場で使える堅牢なプロシージャ実装
では、先ほど作成した `clsScreenLock` を実際の業務ロジックでどのように利用するのか。模範的な実装パターンを示す。
Sub ExecuteHeavyProcess()
‘ 【重要】必ずプロシージャの先頭付近でクラスをローカル変数として宣言する
Dim screenLock As clsScreenLock
Set screenLock = New clsScreenLock
On Error GoTo ErrorHandler
‘ — ここから描画抑制下での高速処理 —
Dim db As DAO.Database
Set db = CurrentDb
‘ 例:重いトランザクション処理や大量レコードのループ
db.Execute “UPDATE T_Target SET Status = 1 WHERE Processed = 0”, dbFailOnError
‘ 途中経過をメッセージで出したい場合は、スコープを抜ける前に手動解放可能
‘ screenLock.ReleaseNow
MsgBox “処理が正常に完了しました。”, vbInformation
‘ — ここまで —
‘ 明示的なオブジェクト破棄(VBAでは推奨)
Set screenLock = Nothing
Exit Sub
ErrorHandler:
‘ エラーが発生しても、ここで screenLock 変数がスコープから外れるため、
‘ VBAが自動的に clsScreenLock の Terminate を呼び出し、Echo True を強制実行する。
MsgBox “エラーが発生しました: ” & Err.Description, vbCritical
Set screenLock = Nothing
End Sub
このコードが担保する安全性
仮に `db.Execute` の実行中にODBCのタイムアウトやロック競合が発生し、コードが `ErrorHandler` へジャンプしたとする。その瞬間、`screenLock` 変数は破棄の対象となり、例外的にジャンプしたルートであっても `Class_Terminate` が確実に実行される。
開発者が「エラーハンドラーの書き忘れで画面がフリーズしたままになった」という人災を起こす余地を、言語仕様のレベルで完全に排除しているのだ。
—
4. シニアエンジニアから次世代への提言
Access VBAは、しばしば「おもちゃの言語」と揶揄される。しかし、それは書き手側の技量が低いが故の誤解に過ぎない。メモリ管理のライフサイクル(Instantiation to Termination)を正確に理解し、VBAの裏側でうごめくCOMコンポーネントの挙動をコントロール下におくことで、Accessは堅牢なエンタープライズ・クライアントへと変貌する。
今回紹介した `clsScreenLock` は、単なる「画面のちらつき防止」のテクニックではない。「予期せぬ例外からシステムの状態を必ず安全な初期値へロールバックさせる」という、プログラミングにおける不変の哲学をVBAというレガシー環境で具現化したものだ。
今すぐ既存のプロジェクトを見直し、散らばった `Application.Echo` をこのクラスに置き換えてほしい。あなたの構築したシステムは、より一層「壊れない要塞」へと近づくだろう。
