【実務・中級編】Application.SysCmdで進捗バーをステータスバーに表示する – Access VBA解析バイブル

スポンサーリンク

【Access VBA】ユーザーを不安にさせない極限のUIフィードバック:`Application.SysCmd` による進捗バー実装の極意

Accessで大量のデータを処理するバッチや、外部システムとの連携APIを走らせる際、画面が「応答なし」になり、ユーザーがフリーズしたと勘違いして強制終了してしまった――。こうしたトラブルを経験したことはないでしょうか。

ユーザーに「今、システムは正常に動いている」と視覚的に伝えることは、業務システムの品質を左右する極めて重要な要素です。

今回は、Accessが標準で提供する最も軽量で堅牢なUIフィードバック手段である`Application.SysCmd`を用いたステータスバーへの進捗表示について解説します。単なる構文紹介にとどまらず、パフォーマンスの低下を防ぐ「描画の間引き」や、エラー発生時にも確実に表示をクリアする「クラスモジュールを活用したRAII(Resource Acquisition Is Initialization)パターン」など、エンタープライズ開発で必須となる実践的な設計手法を伝授します。

1. なぜ自作フォームではなく `SysCmd` なのか?

進捗バーを実装しようとする際、専用のポップアップフォームを作成し、そこにテキストボックスや四角形オブジェクトを配置して進捗バーを自作するアプローチをよく見かけます。

しかし、その設計は以下のリスクを伴います。

1. 描画オーバーヘッドの増大
フォームオブジェクトのプロパティをミリ秒単位で頻繁に書き換えると、Accessの描画スレッド(ウィンドウメッセージの処理)に極大な負荷がかかり、処理速度が数倍から数十倍に低下します。
2. フォーカス奪取とウィンドウ制御の複雑化
モーダルフォームとして開くとメイン処理の制御が複雑になり、モードレスにするとユーザーが他の操作をしてしまい予期せぬエラーを誘発します。

これに対し、Access標準のステータスバーを利用する `Application.SysCmd` は、Access本体のウィンドウマネージャーと直結しているため、極めて軽量かつ安定して動作します。

2. `SysCmd` の基本構造と潜む罠

`SysCmd`による進捗バー制御は、以下の3つのステップで行われます。

‘ 1. 進捗バーの初期化(テキストと最大値を指定)
Application.SysCmd acSysCmdInitMeter, “処理を実行中…”, 100

‘ 2. 進捗状況の更新(現在の値を指定)
Application.SysCmd acSysCmdUpdateMeter, 50

‘ 3. 進捗バーの破棄(必ず実行しなければならない)
Application.SysCmd acSysCmdRemoveMeter

【罠】クリーンアップ漏れによる「ステータスバーのフリーズ」

`acSysCmdInitMeter` を呼び出すと、Accessはステータスバーの制御権を確保します。処理が正常終了、あるいは途中でエラーにより異常終了した際に `acSysCmdRemoveMeter` を確実に呼び出さないと、ステータスバーに進捗バーが残存し、Accessを再起動するまで表示がバグる原因になります。

3. プロの設計:クラスモジュールによる「自動クリーンアップ(RAII)」

エラーハンドリングをどれだけ綿密に書いても、開発者が `On Error GoTo` の先で `acSysCmdRemoveMeter` を書き忘れたり、デバッグ中にコードを強制停止させたりすると、ステータスバーは崩壊します。

これをエレガントに解決するのが、VBAのクラスモジュールの生存期間(ライフサイクル)を利用した設計です。

クラスインスタンスがメモリから消滅する(= プロシージャを抜ける、またはエラーでリセットされる)際に発生する `Class_Terminate` イベントに消去処理を仕込むことで、開発者が意識せずとも絶対にステータスバーが自動復旧する仕組みを構築します。

3.1. プロダクションコード:クラスモジュール `clsProgressBar`

まずは、Accessプロジェクトに新規クラスモジュールを作成し、オブジェクト名を `clsProgressBar` に変更して、以下のコードを貼り付けてください。

Option Compare Database
Option Explicit

‘ =========================================================================
‘ クラス名: clsProgressBar
‘ 概要: Application.SysCmd を安全かつ高パフォーマンスに制御するクラス
‘ =========================================================================

Private m_MaxVal As Long
Private m_CurrentVal As Long
Private m_Title As String
Private m_UpdateInterval As Long ‘ 描画更新を間引くためのインターバル
Private m_IsInitialized As Boolean

‘ — デストラクタ —
‘ クラスのインスタンスが破棄される際(正常終了、エラー終了、強制リセットなど)に自動実行
Private Sub Class_Terminate()
Call CloseProgressBar
End Sub

‘ — メソッド: 開始 —
‘ @param title: ステータスバーに表示する文言
‘ @param maxVal: 処理の総数(ループの最大数など)
‘ @param updateInterval: 何回に1回描画を更新するか(1 = 毎回, 10 = 10回に1回)
Public Sub Initialize(ByVal title As String, ByVal maxVal As Long, Optional ByVal updateInterval As Long = 1)
If maxVal <= 0 Then maxVal = 1 m_Title = title m_MaxVal = maxVal m_UpdateInterval = IIf(updateInterval <= 0, 1, updateInterval) m_CurrentVal = 0 m_IsInitialized = True ' ステータスバーに進捗バーを初期表示 Application.SysCmd acSysCmdInitMeter, m_Title, m_MaxVal End Sub ' --- メソッド: 進捗更新 --- ' @param currentVal: 現在の処理位置 Public Sub Update(ByVal currentVal As Long) If Not m_IsInitialized Then Exit Sub m_CurrentVal = currentVal ' 指定されたインターバル(間引き数)に合致する場合のみ描画を更新する ' これによりループ処理の実行速度低下を極限まで防ぐ If (m_CurrentVal Mod m_UpdateInterval = 0) Or (m_CurrentVal >= m_MaxVal) Then
Application.SysCmd acSysCmdUpdateMeter, m_CurrentVal

‘ OSに処理を逃がし、画面の描画更新とフリーズ検知の回避を行う
DoEvents
End If
End Sub

‘ — メソッド: 明示的クローズ —
Public Sub CloseProgressBar()
If m_IsInitialized Then
Application.SysCmd acSysCmdRemoveMeter
m_IsInitialized = False
End If
End Sub

3.2. プロダクションコード:標準モジュールでの利用例

上記で作成した `clsProgressBar` を、実際の業務ロジックに組み込むコードです。

Option Compare Database
Option Explicit

‘ =========================================================================
‘ 処理概要: 大量レコードの更新バッチ処理
‘ =========================================================================
Public Sub ExecuteBulkUpdate()
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim totalRecords As Long
Dim currentCount As Long

‘ 進捗バークラスのインスタンス生成
Dim progressBar As clsProgressBar
Set progressBar = New clsProgressBar

On Error GoTo Error_Handler

Set db = CurrentDb
‘ サンプルのため、任意のローカルテーブルを読み込む(環境に合わせて変更してください)
Set rs = db.OpenRecordset(“SELECT FROM T_Transaction”, dbOpenDynaset)

‘ レコード総数の取得
If rs.EOF Then
MsgBox “処理対象のデータが存在しません。”, vbInformation
Exit Sub
End If

rs.MoveLast
totalRecords = rs.RecordCount
rs.MoveFirst

‘ 【重要】プログレスバーの初期化
‘ レコード数が数万件に及ぶ場合は、描画を100回に1回(100件ごと)に間引いて高速化
progressBar.Initialize “データを更新中…”, totalRecords, 100

‘ ループ処理開始
currentCount = 0
Do Until rs.EOF
‘ — 疑似的な重い処理(実際はここにアップデート処理等が入る) —
‘ rs.Edit
‘ rs!ProcessedFlag = True
‘ rs.Update
‘ ————————————————————-

currentCount = currentCount + 1

‘ 進捗バーの更新(内部で自動的に描画間引きとDoEventsが実行される)
progressBar.Update currentCount

rs.MoveNext
Loop

MsgBox “処理が正常に完了しました。”, vbInformation, “完了”

Exit_Handler:
‘ クリーンアップ
On Error Resume Next
If Not rs Is Nothing Then rs.Close: Set rs = Nothing
If Not db Is Nothing Then Set db = Nothing

‘ ここで progressBar を Nothing にするか、プロシージャを抜けた瞬間に
‘ クラスの Class_Terminate が走り、自動的かつ確実に SysCmd がクリーンアップされる。
Set progressBar = Nothing
Exit Sub

Error_Handler:
MsgBox “エラーが発生しました。” & vbCrLf & _
“エラー番号: ” & Err.Number & vbCrLf & _
“エラー内容: ” & Err.Description, vbCritical, “システムエラー”
Resume Exit_Handler
End Sub

4. プログラミングリーダーが教える「極限のボトルネック回避策」

この実装パターンには、実務でパフォーマンスを低下させないための2つの高度なテクニックが組み込まれています。

① 描画更新の「間引き(Mod演算)」

画面更新(描画)はOSにとって「非常にコストの高い処理」です。
例えば10万件のレコードをループ処理する際、1周ごとに `SysCmd` と `DoEvents` を実行すると、本来10秒で終わるはずのSQL・データ処理が、描画遅延だけで5分以上かかるようになってしまいます。

上記のクラスでは、`updateInterval` 引数により、指定した頻度(例:100回に1回)だけで描画を更新するように設計しています。

  • 総件数が100件程度:`updateInterval = 1`(毎回更新)
  • 総件数が1万件程度:`updateInterval = 100`(100件ごとに更新)
  • 総件数が10万件以上:`updateInterval = 1000`(1000件ごとに更新)

この単純な「間引き」を行うだけで、進捗バーの動きは滑らかさを保ったまま、処理速度はほぼノーオーバーヘッド(最速値)を叩き出します。

② `DoEvents` の適切なハンドリング

`DoEvents` は、OSに制御を戻して「応答なし(フリーズ)」状態を防ぐために必須ですが、これを乱発すると処理速度が著しく低下します。本設計では、「間引いた描画更新のタイミングと同期してのみ `DoEvents` を実行する」ことで、レスポンス維持と実行速度の極限のバランスを保っています。

まとめ:信頼性の高いコードが、システムへの信頼を生む

`Application.SysCmd` は、Accessに最初から備わっている強力なツールです。しかし、それを生かすも殺すも設計次第です。

  • クラスモジュール化による自動クリーンアップ(RAIIパターン)で、バグによる表示崩れを防ぐ。
  • Mod演算による描画間引きで、プログレスバー自体の負荷を極限まで削減する。

この2点を徹底するだけで、あなたの作成するAccessツールは、一気に「プロが作った堅牢な社内システム」へと昇華します。ぜひ現場のコードに組み込み、その安定性と圧倒的な処理速度を体感してください。

タイトルとURLをコピーしました