【実務・中級編】【中級】Application.SysCmdを活用した、長時間のデータ処理における進捗バーの実装 – Access VBA解析バイブル

スポンサーリンク

Access VBAを掌握する極限の知見:SysCmdで実現する、ノンストップ・プログレスバーの極意

業務アプリケーションの開発現場において、数万件に及ぶマスターデータの更新や、複雑な集計バッチ処理をVBAで走らせたことはないだろうか。
画面がフリーズしたかのように固まり、ユーザーは不安に駆られて無慈悲に「Ctrl + Break」を連打する。そしてアプリケーションは無残にクラッシュし、中途半端に書き換わったデータベースだけが残る――。

これを防ぐのが、プロとしての最低限の美学だ。

今回は、Accessが標準で持っている隠し宝石のようなAPI、`Application.SysCmd`を活用した「堅牢かつ非同期な進捗バー(プログレスバー)」の実装手法を授けよう。ネット上の浅い解説にあるような「場当たり的なコード」ではなく、オブジェクトのライフサイクルとパフォーマンスの限界を知るプロのアーキテクチャを解説する。

1. なぜ「なんちゃって進捗バー」を作ってはいけないのか

進捗バーと聞くと、ユーザーフォーム(UserForm)をわざわざ新規作成し、そこにラベルを並べて `DoEvents` を挟みながら幅をピクセル単位で変更していく……という愚行を思い浮かべる開発者が後を絶たない。

だが、冷静に考えてほしい。

  • フォームの描画コスト: ループのたびにUIの再描画(Paintイベント)が発生し、本来のデータ処理速度が何倍にも低下する。
  • メモリとリソース: フォームのロード・アンロード、オブジェクトのライフサイクル管理が無駄なオーバーヘッドを生む。
  • モダリティの罠: モーダルフォームであれば画面がロックされ、モデルレスであればユーザーが意図しない操作でバグを引き起こす。

我々が使うべきは、Accessのステータスバー領域を直接叩く `Application.SysCmd` だ。
アプリケーションの根幹に組み込まれている機能であるため、余計な描画コストを極限まで削ぎ落とし、極めて高速かつエレガントにステータスを支配できる。

2. SysCmdメソッドのアーキテクチャ

`SysCmd` は、Accessの内部状態を制御・取得するための多目的関数である。進捗バー制御においては、主に以下の3つのアクション引数(第一引数)を使用する。

1. `acSysCmdInitMeter` (1): 進捗バーの初期化(最大値の設定)
2. `acSysCmdUpdateMeter` (2): 進捗バーの現在値の更新
3. `acSysCmdRemoveMeter` (3): 進捗バーの消去(必ず解放すること)

ここで一つ、現場のシニアとして絶対の掟を伝えておく。
「初期化したメーターは、エラーが発生しようとも、処理が正常終了しようとも、必ず最後に `RemoveMeter` で消去しなければならない」
これを怠ると、Accessのステータスバーが異常状態のまま固定され、最悪の場合セッションを再起動する羽目になる。エラーハンドリングの `On Error Goto` との組み合わせは必須条件なのだ。

3. 【プロダクションコード】実務で耐えうる堅牢なデータ処理モジュール

以下のコードは、数万件規模のレコードセットを走査し、安全かつスムーズに進捗バーを更新する実務直結の標準モジュールテンプレートだ。そのままコピー&ペーストしてプロジェクトに組み込んでほしい。

Option Compare Database
Option Explicit

‘ =========================================================================
‘ 模範的バッチ処理プロシージャ:SysCmdによる進捗バー制御
‘ =========================================================================
Public Sub ExecuteHeavyBatchProcess()
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim lngTotalRecords As Long
Dim lngCurrentCount As Long

‘ エラーハンドリングの布石(ステータスバーのゾンビ化を防ぐため必須)
On Error GoTo ErrorHandler

Set db = CurrentDb()

‘ 1. 対象レコードセットの取得(前方スクロールのみの軽量モードでオープン)
‘ ※大量データを扱うため、dbOpenForwardOnly を使用してメモリを節約する
Set rs = db.OpenRecordset(“T_LargeData_Master”, dbOpenForwardOnly, dbReadOnly)

‘ レコードが存在しない場合のガード節
If rs.EOF Then
MsgBox “処理対象のデータが存在しません。”, vbInformation, “情報”
GoTo Cleanup
End If

‘ 2. レコード数の正確な特定
‘ ※dbOpenForwardOnlyのRecordCountは正確ではないため、一旦末尾まで移動してカウントする
rs.MoveLast
lngTotalRecords = rs.RecordCount
rs.MoveFirst

‘ =====================================================================
‘ 3. SysCmdによる進捗バーの初期化
‘ =====================================================================
Call SysCmd(acSysCmdInitMeter, “データを処理しています…”, lngTotalRecords)
lngCurrentCount = 0

‘ 4. メインループ
Do While Not rs.EOF
‘ —————————————————————–
‘ ここに実際の重いデータ処理(UPDATEや外部API連携など)を記述
‘ —————————————————————–
‘ 例: Debug.Print rs!ID
‘ —————————————————————–

lngCurrentCount = lngCurrentCount + 1

‘ 性能劣化を防ぐため、すべてのレコード毎ではなく、例えば100件毎(または適度な間隔)に更新する
‘ ※毎件更新するとUIスレッドが圧迫されるため、モデレートを入れるのがプロの技
If lngCurrentCount Mod 50 = 0 Or lngCurrentCount = lngTotalRecords Then
Call SysCmd(acSysCmdUpdateMeter, lngCurrentCount)
End If

rs.MoveNext
Loop

‘ 5. 正常終了時のクリーンアップ
Call SysCmd(acSysCmdRemoveMeter)

MsgBox “すべての処理が正常に完了しました。” & vbCrLf & _
“処理件数: ” & lngCurrentCount & ” 件”, vbInformation, “完了”

Cleanup:
‘ オブジェクトの厳格な解放(メモリリークの根絶)
On Error Resume Next
If Not rs Is Nothing Then rs.Close: Set rs = Nothing
If Not db Is Nothing Then Set db = Nothing
Exit Sub

ErrorHandler:
‘ 異常終了時も必ずステータスバーのメーターを消去する
Call SysCmd(acSysCmdRemoveMeter)

MsgBox “予期せぬエラーが発生しました。” & vbCrLf & _
“エラー番号: ” & Err.Number & vbCrLf & _
“エラー内容: ” & Err.Description, vbCritical, “致命的エラー”
Resume Cleanup
End Sub

4. コードの深掘りとアーキテクチャの解説

このコードには、現場で幾多の修羅場をくぐり抜けてきたエンジニアの知見が凝縮されている。

① `dbOpenForwardOnly` と `dbReadOnly` の選択

大規模データを扱う際、デフォルトのダイナセット(`dbOpenDynaset`)でレコードセットを開くのは「メモリの無駄遣い」であり「パフォーマンスの殺人」だ。
前方(下方向)にしか移動しないことが確実なバッチ処理であれば、`dbOpenForwardOnly` を指定せよ。これによってAccess内部の変更バッファが生成されず、爆発的な速度向上をもたらす。

② 更新頻度のモデレート(間引き制御)

コード内の `If lngCurrentCount Mod 50 = 0 Then` に注目してほしい。
もし数百万件のレコードを処理する場合、すべてのレコードで `SysCmd(acSysCmdUpdateMeter)` を呼ぶと、OSの描画キューがVBAの実行スレッドに負けてしまい、逆に処理全体が遅くなる。
「人間の目が視認できる限界」と「パフォーマンス」のバランスを取り、適度な間隔(数件〜数十件に1回)でステータスを更新するのが、真のパフォーマンスチューニングである。

③ 徹底的なリソースのライフサイクル管理

VBAにおける最大の悪徳は「オブジェクトの放置」だ。
`Recordset` や `Database` を開きっぱなしにするコードは、ガベージコレクションを信用しきった素人の発想にすぎない。`Cleanup` ラベルを用意し、いかなる場合でも確実にメモリからパージする構造を徹底すること。

5. まとめ

Access VBAは、正しく使えば今なお強力な「高速プロトタイピング&業務自動化ツール」である。
しかし、その手軽さゆえに「動けばいいや」という雑なコードが量産され、保守性を失って現場の負債と化していく。

今回紹介した `Application.SysCmd` による進捗バーの実装は、単なる見た目の演出ではない。「システムが生きていることをユーザーに示し、無用なトラブルを防ぐ」という、エンジニアの責任感の具現化なのだ。

あなたの書くコードに、プロとしての誇りと美学を宿せ。

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