Access VBAを掌握する極限の知見:Application.Echoを制し、画面のちらつきと「永遠のフリーズ地獄」を根絶せよ
開発現場でよく見かける光景がある。大量のレコードを処理する際、画面がバチバチと点滅し、タスクマネージャーを開きたくなるようなノロノロ運転を続けるAccessフォーム。これを解決するために先輩が残していったコードがこれだ。
Application.Echo False
‘ — 重い処理 —
Application.Echo True
今すぐこの書き方をやめなさい。
プログラミング経験の浅い開発者が最もやりがちな罠がこれだ。もしこの「— 重い処理 —」の内部で予期せぬ実行時エラー(ゼロ除算、レコードロック、存在しないオブジェクトの参照など)が発生したらどうなるか?
VBAの処理はそこで停止し、`Application.Echo True` に到達することはない。結果として、Accessの画面描画は永久に停止したまま(=画面が真っ白、あるいは更新されない幽霊状態)になり、ユーザーは「固まった!」とパニックになって強制終了する。運悪く未保存のデータがあれば、阿鼻叫喚の地獄絵図の完成だ。
今回は、プロの現場で生き残るための「絶対に画面描画の復帰を担保する汎用クラス」の設計思想と実装を授けよう。
—
なぜ「エラー処理付きプロシージャ」では不十分なのか?
「それなら、各プロシージャに `On Error GoTo ErrorHandler` を書けばいいじゃないか」と思ったそこのあなた。それはコードの重複を生む悪手の典型だ。
すべての重い処理のプロシージャに以下のようなボイラープレート(お決まりのコード)を書き散らすのか?
Sub DoSomething()
On Error GoTo ErrorHandler
Application.Echo False
‘ 業務ロジック…
Application.Echo True
Exit Sub
ErrorHandler:
Application.Echo True ‘ 忘れがち!
MsgBox Err.Description
End Sub
人間の記憶力や注意力に依存した設計は、遅かれ早かれバグを生む。「画面描画の抑制と復帰」というライフサイクルは、VBAの言語仕様である「クラス(Class)」のコンストラクタとデストラクタの概念に委ねるべきなのだ。
—
解決策:RAIIパターンをAccess VBAに持ち込む
C++などのモダン言語には、スコープを抜けるときに確実にリソースを解放する「RAII(Resource Acquisition Is Initialization)」という概念がある。
VBAには厳密なデストラクタはないが、クラスのインスタンスが破棄される瞬間(Class_Terminateイベント)を利用すれば、これと全く同じ堅牢性を実現できる。
つまり、「Echoを止めるクラス」のインスタンスが存在している間だけ描画を止め、インスタンスが消滅する(=変数から外れる、あるいはエラーでプロシージャを抜ける)瞬間に、VBAのランタイムが自動的に `Echo True` を実行する仕組みを作るのだ。
クラスモジュールの設計
VBAエディタで新しいクラスモジュールを追加し、名前を `clsScreenUpdater` とリネームしてほしい。
1. クラスモジュール:`clsScreenUpdater` のコード
VERSION 1.0 CLASS
BEGIN
MultiUse = -1 ‘False
END
Attribute VB_Name = “clsScreenUpdater”
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
Option Explicit
‘ =================================================================
‘ クラス名: clsScreenUpdater
‘ 概要: Application.Echoのライフサイクルを管理し、エラー時でも確実に復帰させる
‘ =================================================================
Private m_IsActive As Boolean
Private Sub Class_Initialize()
‘ インスタンス生成時に描画を停止
On Error Resume Next
Application.Echo False
m_IsActive = (Err.Number = 0)
On Error GoTo 0
End Sub
Private Sub Class_Terminate()
‘ インスタンス破棄(スコープアウト・エラー終了含む)時に確実に描画を復帰
If m_IsActive Then
On Error Resume Next
Application.Echo True
On Error GoTo 0
End If
End Sub
‘ 明示的に早期復帰させたい場合のメソッド(通常は不要だが保険として用意)
Public Sub RestoreNow()
If m_IsActive Then
Application.Echo True
m_IsActive = False ‘ 二重実行防止
End If
End Sub
たったこれだけだ。だが、この数行のコードが、あなたのアプリケーションをプロフェッショナルな品質へと引き上げる。
—
実戦投入:プロダクションコードでの使い方
では、実際にこのクラスを業務ロジックでどのように使うのかを見ていこう。標準モジュールやフォームのコードから、以下のように呼び出す。
Sub HeavyProcessSample()
‘ 1. クラスのインスタンスを変数に格納(この瞬間に Echo False が走る)
Dim screenLock As clsScreenUpdater
Set screenLock = New clsScreenUpdater
‘ — ここから爆速・非描画エリア —
‘ わざとエラーを起こしてみるテスト(例:ゼロ除算)
Dim x As Integer
x = 10 / 0 ‘ ←ここでエラーが発生!
‘ 大量データの処理やフォームの書換がここに続く…
DoEvents
‘ — ここまで —
‘ 2. プロシージャを抜ける際に screenLock 変数がスコープアウトし、
‘ Class_Terminate が発動して自動的に Echo True が実行される。
‘ 万が一エラーで止まっても、確実に画面は復帰する。
End Sub
この設計がもたらす圧倒的なメリット
1. エラーハンドリングからの解放:
処理途中でどんな致命的なエラーが発生しようとも、VBAのガベージコレクタが変数を解放するタイミングで `Class_Terminate` が走るため、「画面が固まったまま戻らない」というクレームを100%根絶できる。
2. コードの可読性と保守性の劇的向上:
「どこでEchoを止めて、どこで戻したっけ?」とコードを目で追う必要がない。変数を宣言したスコープ(このプロシージャ内)だけでライフサイクルが完結する。
3. ステートの汚染防止:
二重にEchoを制御してしまうようなスパゲッティコードを防ぐことができる。
—
チーフアーキテクトからの実務アドバイス
Accessでの業務システム開発において、パフォーマンスチューニングとUIの安定性は表裏一体だ。特にネットワーク経由(ファイルサーバなど)でデータベースを共有している環境では、不要な画面描画の発生がそのままネットワーク帯域とCPUの無駄遣いになり、ロック競合のリスクを高める。
`Application.Echo False` を使うときは、必ず今回紹介した RAIIパターン(クラスによるライフサイクル管理) をセットで導入しなさい。
「動けばいい」というアマチュアのコードから脱却し、エラータフで美しい、プロフェッショナルなアーキテクチャをあなたのAccessアプリケーションに実装してほしい。
