こんにちは!Access VBAの奥深い世界へようこそ。チーフアーキテクトの私です。
マクロのボタンを押したはいいものの、画面がピクリとも動かなくなり、「これ、フリーズしてるの?それとも頑張って計算中なの…?」と、冷や汗をかきながら画面の前で祈った経験はありませんか?
数万件ものデータをガッツリ処理するバッチ処理において、「ユーザーを不安にさせないこと」は、プロのエンジニアにとって極めて重要な作法です。
今回は、Access標準の隠し武器とも言える `Application.SysCmd` メソッドを駆使して、ステータスバーに美しく滑らかな進捗バー(プログレスバー)を実装する方法を伝授します。ここをクリアすれば、あなたの作るAccessアプリは一気に「プロのシステム」へと生まれ変わりますよ。さあ、一緒に扉を開けましょう!
—
なぜ `SysCmd` なのか?(オブジェクトモデルの知見)
Access VBAで進捗バーを作ろうとすると、フォームに「カニ歩き」させるような独自のバーを作りがちです。しかし、重いデータ処理の最中に複雑なフォームの再描画(Repaint)を行うと、それ自体がパフォーマンスのボトルネック(足かせ)になります。
そこで登場するのが、Accessのアプリケーション自体が持っている機能、`Application.SysCmd` です。
ステータスバーという「OSおよびAccessが管理する領域」を直接叩くため、余計な描画負荷をかけず、極めて軽量かつ確実に進捗を伝えることができます。
—
実装の全体像とステップ
進捗バーを正しく機能させるためのステップは、たったの3つです。
1. 初期化(Initialization):バーの「ゼロ地点」を設定し、タイトルを決める。
2. 更新(Update):レコードを1件処理するごとに、バーの目盛りを1つ進める。
3. 終了(Removal):処理が終わったら、ステータスバーを元の綺麗な状態に戻す。
これをコードに落とし込むと、美しいライフサイクルが完成します。
—
実践!コピペで使える極上のサンプルコード
以下のコードは、実際の業務システムでそのまま使える堅牢なテンプレートです。
今回は `CurrentDb` を使ってテーブルの全レコードを走査するシーンを想定しています。
Public Sub RunHeavyDataProcessWithProgress()
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim lngTotalCount As Long
Dim lngCurrentCount As Long
‘ データベースとレコードセットの参照を取得
Set db = CurrentDb
Set rs = db.OpenRecordset(“T_大規模データ”, dbOpenSnapshot)
‘ レコードが空の場合は即座に抜ける(ゼロ除算や無駄な処理を防ぐ防衛策)
If rs.RecordCount = 0 Then
MsgBox “処理対象のデータが存在しません。”, vbInformation, “お知らせ”
GoTo Cleanup
End If
‘ 【重要】正確な全件数を取得するために最後まで移動する
rs.MoveLast
lngTotalCount = rs.RecordCount
rs.MoveFirst
‘ —————————————————————–
‘ Step 1: SysCmdによる進捗バーの初期化
‘ 引数 4 は「進捗バーの表示」、タイトル、最大値を指定します
‘ —————————————————————–
SysCmd acSysCmdInitMeter, “データを精査・処理しています…”, lngTotalCount
lngCurrentCount = 0
‘ メインのループ処理
Do Until rs.EOF
‘ ————————————————————-
‘ ここに実際の重い処理(データの加工、他テーブルへの書き込みなど)を書く
‘ ————————————————————-
‘ 例として、ダミーのウェイト(処理の擬似再現)は入れず、そのまま進めます
‘ カウントを進める
lngCurrentCount = lngCurrentCount + 1
‘ ————————————————————-
‘ Step 2: 10件に1回など、適度な頻度でバーを更新(パフォーマンス最適化)
‘ 毎件更新するとVBAの描画が追いつかず遅くなるため間引くのがプロの技
‘ ————————————————————-
If lngCurrentCount Mod 10 = 0 Or lngCurrentCount = lngTotalCount Then
SysCmd acSysCmdUpdateMeter, lngCurrentCount
End If
rs.MoveNext
Loop
MsgBox “すべての処理が正常に完了しました!”, vbInformation, “完了”
Cleanup:
‘ —————————————————————–
‘ Step 3: 必ずステータスバーをクリアして初期状態に戻す
‘ これを忘れると、Accessの下部に変なバーが残り続けます(重要!)
‘ —————————————————————–
SysCmd acSysCmdRemoveMeter
‘ オブジェクトの解放(メモリリークの根絶)
If Not rs Is Nothing Then rs.Close: Set rs = Nothing
Set db = Nothing
End Sub
—
コードの急所:ここが分かればもう迷わない!
1. `rs.MoveLast` の意味を知る
DAOのレコードセットを開いた直後、`RecordCount` プロパティは「実際にアクセスした件数」しか正確に返さないことがあります(特にダイナセット型など)。そのため、一度 `MoveLast` で最後のレコードまで強制的にジャンプさせ、テーブル全体の総数を正確に把握してから進捗バーの最大値(`lngTotalCount`)に設定するのが鉄則です。
2. `Mod`(剰余演算子)による描画の間引き
数万件もあるループの中で、毎回 `SysCmd acSysCmdUpdateMeter` を呼ぶと、VBAの処理速度がステータスバーの描画に引っ張られて数倍遅くなります。
`If lngCurrentCount Mod 10 = 0` を使うことで、「10件に1回だけ画面を更新する」という賢い間引きを行い、処理速度を落とさずに滑らかなバーを実現できます。この気配りがエンジニアの腕の見せ所です。
3. クリーニング(`acSysCmdRemoveMeter`)の絶対厳守
エラーが起きた時や処理が途中で終わった時でも、必ずステータスバーを元に戻さなければなりません。サンプルコードのように `GoTo Cleanup` などのエラーハンドリング・終了ルートを必ず用意し、`acSysCmdRemoveMeter` を通る構造にしてください。
—
よくある落とし穴とエラー対策
- 「ステータスバーに何も表示されない?」
Accessのオプションで「ステータス バーを表示する」のチェックが外れていると見えません。アプリ全体の仕様としてステータスバーが表示設定になっていることを確認してください。
- 「途中でフリーズしたように止まる」
処理があまりに重い場合、VBAがOSに制御を返さなくなることがあります。あまりに巨大なループの場合は、適宜 `DoEvents` 関数を挟むことでOSのフリーズを回避できますが、`SysCmd` を使っていればある程度システムが描画の呼吸をしてくれます。
—
おわりに:ここをクリアすれば、Access VBAの基本はバッチリですよ
いかがでしたでしょうか?
単に動くだけのコードから、「使う人の心地よさ」まで配慮したコードへ。このステップを踏み出したあなたなら、もうマクロの記録や初歩的な記述で躓くことはありません。
大規模なデータを扱う現場において、ユーザーとの対話(フィードバック)をデザインすることは極めて価値の高いスキルです。ぜひ今日のコードをご自身のAccess環境にブートストラップして、その滑らかな動きを体感してみてください。
それでは、次回のアーキテクチャ解説でお会いしましょう。あなたのAccess開発ライフが素晴らしいものになりますように!
