「エラーを握りつぶすな、予測せよ」Access開発における防御的プログラミングの鉄則
Accessで業務ツールを開発していると、必ず突き当たる壁がある。それは「存在しないオブジェクトへのアクセス」による実行時エラーだ。
特に多いのが、ユーザーの操作で呼び出されるフォームの起動処理。`DoCmd.OpenForm` を何の疑いもなく記述してはいないだろうか? 開発者が削除したり名前を変えたりした途端、エンドユーザーの画面には無機質なエラーダイアログが表示される。これこそが、素人とプロの境界線だ。
今回は、`Application.CurrentProject.AllForms` を活用し、システムの堅牢性を極限まで高めるための「防御的プログラミング」の真髄を伝授する。
—
1. なぜ「エラーハンドリング」だけでは不十分なのか
初心者はよく「On Error Resume Next」でエラーを無視したり、`Err.Number` を拾ってメッセージを出すだけの手法をとる。だが、それは「事後対応」に過ぎない。
真に優れたシステムは、「そもそもエラーが発生しない状況をコードで保証する」ものだ。`DoCmd.OpenForm` を叩く前に、そのフォームが本当にそこに存在するかを確認する。この一手間が、保守コストを劇的に下げる。
—
2. 存在確認のベストプラクティス:CurrentProject.AllForms
`CurrentProject.AllForms` コレクションは、データベース内の全フォーム情報へのアクセスポイントだ。これを使えば、Accessの内部構造を深く知らなくても、安全に存在チェックができる。
以下のコードは、現場でそのまま使える「堅牢なフォーム起動用ユーティリティ」のテンプレートだ。
‘ —————————————————————————
‘ 関数名: SafeOpenForm
‘ 概要: 指定したフォームが存在するか確認し、存在する場合のみ開く
‘ 引数: formName – 開きたいフォーム名
‘ 戻り値: True – 成功 / False – 失敗
‘ —————————————————————————
Public Function SafeOpenForm(ByVal formName As String) As Boolean
Dim objForm As AccessObject
‘ CurrentProject.AllForms コレクションから対象を探索
‘ 存在しない場合はループを回す前にチェックするのがスマート
For Each objForm In CurrentProject.AllForms
If objForm.Name = formName Then
‘ フォームが存在することを確認
DoCmd.OpenForm formName
SafeOpenForm = True
Exit Function
End If
Next objForm
‘ 存在しなかった場合のログ出力や警告
Debug.Print “警告: フォーム ‘” & formName & “‘ が見つかりません。”
MsgBox “システムエラー: 指定された画面が見つかりません。” & vbCrLf & _
“開発者へ連絡してください。”, vbCritical, “設定エラー”
SafeOpenForm = False
End Function
このコードの「設計思想」
1. 状態のチェック(Validation): 処理を実行する前に、システムがその要求に耐えうるかを判断している。
2. 早期リターン(Early Return): 存在が確認できた時点で `Exit Function` することで、不要なループを回さない。
3. 疎結合の維持: フォーム名を引数で渡す設計にすることで、呼び出し元を選ばない再利用性の高いモジュールになっている。
—
3. さらなる高みへ:パフォーマンスへの意識
`For Each` ループは、フォーム数が数十個程度なら全く問題ない。しかし、巨大な業務システムで数百のオブジェクトを抱えている場合、ループを回すこと自体がわずかながらコストになる。
もし、さらにパフォーマンスを突き詰めるのであれば、以下のような「インデックスによる検索」を検討するのも手だ。
‘ より高速にアクセスする方法(名前をキーとして直接参照)
Public Function IsFormExists(ByVal formName As String) As Boolean
On Error Resume Next
‘ 直接名前で参照を試みる。存在しなければエラーが発生する
Dim obj As AccessObject
Set obj = CurrentProject.AllForms(formName)
IsFormExists = (Err.Number = 0)
Err.Clear
End Function
この手法は、`CurrentProject.AllForms` コレクションの内部インデックスを直接叩くため、大規模なプロジェクトでも極めて高速だ。ただし、「エラーを意図的に発生させて状態を判定する」ため、エラーハンドリングの理解が浅いメンバーがいる環境では、前述のループ処理の方が可読性が高く安全と言える。
—
結論:コードは「対話」である
Access開発において、コードは単なる命令ではなく、将来の自分や、システムを引き継ぐ後輩との対話だ。「なぜこのフォームは開かないのか?」「なぜエラーが出るのか?」という問いを、システムが自ら答えられる状態にしておくこと。
それが、伝説的な自動化エンジニアが共通して持つ「防衛的な美学」である。
明日の朝、君のプロジェクトの全 `DoCmd.OpenForm` を確認してほしい。もしそこに `If` 文がなければ、君は今日、一つ大きな改善のチャンスを得たことになる。
健闘を祈る。
