【Access VBAを掌握する極限の知見】Application.Echoを制す者は、UIのストレスを制す
開発プロジェクトの現場で、こんなコードを見たことはないだろうか。
Application.Echo False
‘ — 膨大なレコード処理やクエリ実行 —
Application.Echo True
一見、画面のちらつき(スクリーンの再描画)を抑止する定石に見える。しかし、この書き方をしている時点で、そのシステムは「時限爆弾」を抱えていると言わざるを得ない。
もし、この「膨大なレコード処理」の最中に予期せぬ実行時エラー(例えば、ゼロ除算、レコードロックの競合、オブジェクトの不在など)が発生したらどうなるか?
エラーハンドラーにジャンプした際、あるいはそのままデバッグモードに突入した際、`Application.Echo False` の状態は保持されたままになる。
結果どうなるか。画面は真っ白(あるいは直前の描画でフリーズした状態)のまま、Accessの裏で処理が停止する。ユーザーは「固まった!」とパニックになり、タスクマネージャーから強制終了する羽目になる。そして、二度とそのツールを使ってくれなくなる。
プロフェッショナルなエンジニアたる者、描画抑制の制御を「祈り(エラーが起きないことへの期待)」に頼ってはならない。いかなる例外やエラーが発生しようとも、確実に画面描画を復帰させる「鉄壁のライフサイクル管理」を実装する。
今回は、VBAのクラスモジュールを用いた、エラー耐性抜群の `Application.Echo` 制御メカニズムを授けよう。
—
なぜ「通常のプロシージャ」ではEcho制御に失敗するのか?
VBAにはJavaやC#のような `try-finally` 構文がない。そのため、以下のような構造になりがちだ。
Sub BadExample()
On Error GoTo ErrorHandler
Application.Echo False
‘ 処理…
Err.Raise 1000 ‘ 意図的なエラー
Application.Echo True
Exit Sub
ErrorHandler:
MsgBox “エラー発生: ” & Err.Description
‘ ここでEcho Trueを書き忘れる、あるいはエラー落ちすると…
‘ 画面がブラックアウトしたままになる!
Application.Echo True
End Sub
このアプローチの欠点は、「開発者がエラーハンドラーの記述を忘れた瞬間」「想定外の致命的エラーでVBAがクラッシュした瞬間」に破綻することだ。また、複数のプロシージャがネストして呼び出される実務のコードベースでは、どこでEchoがオフになり、どこでオンに戻すべきかのスコープが完全に崩壊する。
解決策:クラスの「デストラクタ(Terminate)」に復帰処理を委譲する
ここで、オブジェクト指向のライフサイクルを利用する。
VBAのクラスモジュールには、インスタンスが破棄される瞬間(スコープを抜けるとき、あるいは変数がNothingになったとき)に自動実行される `Class_Terminate` イベントが存在する。
これを利用し、「クラスが生きている間はEchoをオフにし、クラスが消滅する(スコープから外れる)瞬間に、異常系・正常系を問わず強制的にEchoをオンに戻す」という設計を採用する。
VBAのガーベージコレクションとオブジェクトの寿命を完全に掌握した、極限まで堅牢なコードを公開しよう。
—
実装コード:`clsEchoController` クラスモジュール
以下のコードを、VBAエディターで新規クラスモジュールを作成し、名前を `clsEchoController` として保存してほしい。
VERSION 1.0 CLASS
BEGIN
MultiUse = -1 (True)
END
Attribute VB_Name = “clsEchoController”
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
‘ ==============================================================================
‘ クラス名: clsEchoController
‘ 概要: Application.Echoのライフサイクルを安全に管理するクラス
‘ 備考: インスタンスが生存している間は画面描画を停止し、
‘ スコープアウト(消滅)時に必ず描画を復帰させる。
‘ ==============================================================================
Option Explicit
Private m_IsActive As Boolean
‘ クラス初期化時にEchoをオフにする
Private Sub Class_Initialize()
On Error Resume Next
m_IsActive = True
Application.Echo False
‘ ステータスバーへのメッセージ表示などを併用すると親切
‘ Application.Echoオフ時はステータスバー更新も止まる点に注意
On Error GoTo 0
End Sub
‘ クラス破棄時(スコープ抜け、エラー発生時、正常終了時)に必ずEchoをオンに戻す
Private Sub Class_Terminate()
On Error Resume Next
If m_IsActive Then
Application.Echo True
‘ 必要に応じてステータスバーをクリア
Application.StatusBar = “”
End If
On Error GoTo 0
End Sub
‘ 明示的に早期解放したい場合の手動メソッド
Public Sub Release()
‘ 自発的にTerminateのロジックを走らせる
Class_Terminate
m_IsActive = False
End Sub
—
現場でどう使うか?:実務でのプロダクションコード例
標準モジュールやフォームのコードから、このクラスをどのように呼び出すべきか。
実務で頻出する、大量データのトランザクション処理を想定したコードを示す。
‘ ==============================================================================
‘ 標準モジュール: 業務処理実行サンプル
‘ ==============================================================================
Option Explicit
Sub ExecuteHeavyProcess()
‘ 1. Echo制御クラスのインスタンスを生成
‘ この瞬間に Application.Echo False が実行される
Dim echoCtrl As New clsEchoController
On Error GoTo ErrorHandler
‘ — 実際の重い処理(例:ループ処理や一括更新) —
Dim i As Long
For i = 1 To 10000
‘ プログレスバーの更新やレコード処理
‘ 例として意図的なエラーを発生させてみる
If i = 5000 Then Err.Raise 9999, , “想定外のトランザクションエラー”
Next i
‘ 正常終了時はここでプロシージャを抜けるが、
‘ echoCtrl変数がスコープから外れる(End Subに到達する)ため、
‘ 自動的にClass_Terminateが走り、Application.Echo Trueに戻る。
MsgBox “処理が正常に完了しました。”, vbInformation
Exit Sub
ErrorHandler:
‘ エラーハンドリング
MsgBox “エラーが発生しました: ” & Err.Description, vbCritical
‘ 【重要】ここで手動で Application.Echo True を書く必要は「ない」。
‘ エラーフッターでこのままプロシージャを抜けても、
‘ 確実にクラスの消滅と同時に画面描画は復帰する。
‘ 必要であればここで終了処理
End Sub
—
者プロフェッショナルエンジニアからの設計上のアドバイス
1. 「何もしないことの価値」をコードに語らせる
エラーハンドラーのたびに `Application.Echo True` を書き散らすコードは、保守フェーズにおいて必ず「書き忘れのバグ」を生む。ライフサイクル管理をクラスにカプセル化することで、ビジネスロジック側は「描画の復帰を意識する必要から解放される」。これが設計の美学であり、堅牢性だ。
2. ステータスバーとの併用注意点
`Application.Echo False` を実行すると、画面の描画だけでなく、`SysCmd` や `Application.StatusBar` による進捗表示の更新も停止、あるいは無視される場合がある。もしユーザーに「処理中…」というフィードバックを与えたい場合は、Echoを落とす前にメッセージを表示するか、画面描画のコントロールとメッセージ出力を混同しないように設計すること。
3. マルチインスタンス・ネストへの耐性
今回紹介したシンプルなフラグ管理でも通常の実務では十分だが、仮に別のユーティリティ関数内ですでにEchoが制御されているケースなどを完璧に考慮する場合でも、このクラスベースの設計であれば、インスタンスごとのスコープが独立するため、安易にグローバルな状態を上書きし合うバグを防ぎやすい。
「動けばいい」のコードから、「エラーが起きても絶対に壊れない」プロフェッショナルなコードへ。
今日の改修から、この `clsEchoController` をあなたのAccessプロジェクトに導入してほしい。コードの品格と安定性が劇的に変わることを約束しよう。
