Access VBAを掌握する極限の知見:DoCmd.RunCommandによる標準機能の「完全同期的制圧」
こんにちは。開発プロジェクトの現場で、数々のAccess地獄をシステムアーキテクチャの力でねじ伏せてきたチーフアーキテクトの私だ。
Access VBAでフォームを操作しているとき、こんな壁にぶつかったことはないだろうか。
- 「今いじっているレコードを安全に保存したいだけなのに、トランザクションやダーティフラグの制御でコードが汚くなる…」
- 「フィルターの解除やレコードの削除を自前で実装しようとしたら、予期せぬエラーやゴミデータが残る…」
素人プログラマは、こうした要件に対してSQLを直に叩いたり、レコードセットをこねくり回したりして自前で車輪の再発明を行おうとする。結果はどうなるか? 「動くには動くが、保守不能なスパゲッティコードの完成」だ。
忘れないでほしい。Accessには、Microsoftが何十年もかけて最適化し磨き上げてきた「最強の標準機能(ビルトインコマンド)」が最初から備わっている。
それをVBAからスマートに呼び出すことこそが、バグの起きない堅牢なシステムを最速で構築するための唯一の正解なのだ。
今回は、`DoCmd.RunCommand` を駆使してAccess標準機能を掌握し、フォーム操作の自動化を極限まで洗練させるテクニックを伝授する。
—
なぜ「自前実装」は悪手であり、なぜ `RunCommand` なのか?
例えば、「現在のレコードの変更を保存し、次のレコードへ移動する」という処理を考えてみよう。
素人コードはこうなる。
‘ 【悪手】自前でゴリゴリ書いた破綻寸前のコード
Me.Dirty = False ‘ これで保存できるが…
‘ ここでバリデーションエラーや排他制御が起きたときのハンドリングが地獄と化す
DoCmd.GoToRecord , , acNext
これの何が問題か?
Accessのフォームには、ユーザーが画面上で操作したときに走る「入力検証(BeforeUpdate等)」「エラーハンドリング」「UIのフォーカス制御」「アンドゥスタックの管理」という高度なイベントチェーンが存在する。自前でロジックを組むと、このAccess標準のライフサイクルを破壊し、「データ不整合」や「謎の実行時エラー」の温床になるのだ。
一方、`DoCmd.RunCommand` は、ユーザーが画面上のボタンを押した挙動と「全く同じシグェンス」をAccessのエンジンレベルで実行する。
つまり、Accessのコアが持つ堅牢なバリデーションや排他制御の恩恵を100%受けたまま、処理を完全に自動化できるというわけだ。
—
実務で即採用できる!プロダクション・コード例
ここからは、実際の業務アプリケーションで必ず直面する要件を、極めて堅牢かつエレガントに実装したコードを公開する。そのままコピー&ペーストしてプロジェクトに組み込んでほしい。
1. 「安全なレコード保存」と「新規追加」の鉄壁ルーチン
ユーザーが入力途中で他の処理に移ろうとした際、ダーティ状態(未保存)のまま強制移動するとエラーになるか、意図しないデータロスが起きる。これを一発で解決する。
Option Compare Database
Option Explicit
”’
”’
Public Sub SafeSaveCurrentRecord()
On Error GoTo ErrorHandler
‘ フォームが編集中(ダーティ状態)の場合のみ保存コマンドを発行
If Me.Dirty Then
‘ acCmdSaveRecord は「レコードの保存」標準コマンド
DoCmd.RunCommand acCmdSaveRecord
End If
Exit Sub
ErrorHandler:
‘ ユーザーによるバリデーションエラーなどで保存が拒否された場合のトラップ
If Err.Number = 2046 Then
MsgBox “現在は保存を実行できません。入力内容を確認してください。”, vbExclamation, “システム警告”
Else
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical, “エラー”
End If
End Sub
2. フォームの「フィルタ完全解除」と「再クエリ」
検索機能を実装した際、複雑に入り組んだフィルター条件や並び替えをきれいにクリアし、初期状態に戻すためのスニペットだ。
”’
”’
Public Sub ResetFormFilter()
On Error GoTo ErrorHandler
‘ フィルターが適用されている場合のみ解除コマンドを実行
If Me.FilterOn Then
‘ acCmdRemoveFilterSort は「フィルタ/ソートの解除」標準コマンド
DoCmd.RunCommand acCmdRemoveFilterSort
End If
‘念のため最新のレコードソースを再クエリ
Me.Requery
Exit Sub
ErrorHandler:
‘ 適用されていないコマンドを実行した際のエラー(2046)をガード
If Err.Number = 2046 Then
‘ フィルタがかかっていない状態での実行は無視する
Resume Next
Else
MsgBox “フィルタ解除中にエラーが発生しました: ” & Err.Description, vbCritical, “エラー”
End If
End Sub
3. トランザクションと連動する「安全なレコード削除」
削除ボタンを押した際、単に `DoCmd.RunCommand acCmdDeleteRecord` を叩くだけでは不十分だ。必ず「本当に削除してよいか」の確認と、関連テーブルへの影響を考慮した堅牢なラッパー関数を作るべきである。
”’
”’
Public Sub SafeDeleteCurrentRecord()
On Error GoTo ErrorHandler
‘ レコードが存在しない場合のガード
If Me.NewRecord Then
MsgBox “削除するレコードがありません。”, vbInformation, “確認”
Exit Sub
End If
‘ ユーザーへの最終確認
If MsgBox(“現在のレコードを完全に削除しますか?この操作は元に戻せません。”, _
vbQuestion + vbYesNo + vbDefaultButton2, “削除の確認”) = vbNo Then
Exit Sub
End If
‘ Access標準の「レコードの削除」コマンドを呼び出し
‘ ※削除確認の標準ダイアログを抑制したい場合は事前に Warnings を False にする手法もあるが、
‘ 誤操作防止のため標準コマンドの挙動(警告ダイアログ)をそのまま活かすのがセオリー。
DoCmd.RunCommand acCmdDeleteRecord
Exit Sub
ErrorHandler:
Select Case Err.Number
Case 2046
MsgBox “現在、このレコードは削除できません。”, vbExclamation, “エラー”
Case Else
MsgBox “削除処理中にエラーが発生しました: ” & Err.Description, vbCritical, “致命的エラー”
End Select
End Sub
—
アーキテクトが教える、実装上の「致命的な罠」と回避策
`DoCmd.RunCommand` は極めて強力だが、プログラマが陥りがちな「罠」が存在する。これを理解していないと、テスト環境では動くのに本番環境で突然クラッシュするシステムの爆弾を抱えることになる。
罠1:定数(`acCmd…`)が使えない、または実行時エラー(2046)になる
`RunCommand` の最大の弱点は、「現在のフォーカス位置やフォームの状態によって、実行できるコマンドとできないコマンドが存在する」という点だ。
例えば、テキストボックスにフォーカスがある状態と、レコードセレクタ(左端のグレーの部分)にフォーカスがある状態では、使える `acCmd` が異なる。
- 対策:
コマンドを実行する前に、必ず対象のコントロールやフォームへ確実に `Me.ControlName.SetFocus` 等でフォーカスを誘導するか、上記コードのように `On Error Resume Next` または `Err.Number = 2046`(そのコンテキストではコマンドが無効というエラー)を厳密にトラップすること。
罠2:画面描画のちらつき(フリッカ)とパフォーマンス劣化
ボタンクリックイベント等で `RunCommand` を連続して発行すると、画面が激しくちらつき、ユーザーにストレスを与える。
- 対策:
処理の開始と終了で `Application.Echo False` と `Application.Echo True` を挟み込み、描画を一時停止させるのがプロの技だ。
Application.Echo False ‘ 画面描画停止
‘ — 複数バッチ的な RunCommand 処理 —
Application.Echo True ‘ 描画再開
—
総括:Accessの本質をリスペクトせよ
プログラミングの世界では「車輪の再発明をするな」と言われるが、Access開発においてそれは何倍も重みを持つ。
Accessという巨大なデータベース&UIフレームワークが持つ「標準機能」を無視して、自前で複雑なロジックを構築するのは、自らバグの山を築いているようなものだ。
`DoCmd.RunCommand` を使いこなすことは、「Accessのエンジンと対話し、その機能を最大限にリスペクトして動かすこと」に他ならない。
この知見をあなたのプロジェクトにインストールし、誰もが嫉妬するような堅牢で高速な業務システムを構築してほしい。健闘を祈る。
