【実務・中級編】Application.CurrentProject.AllFormsを利用した、存在しないフォームへのアクセスを未然に防ぐ防御的プログラミング – Access VBA解析バイブル

スポンサーリンク

【Access VBAを掌握する極限の知見】存在しないフォームへの恐怖を断つ:`AllForms`による完全なる防御的UI制御

現場のAccess開発で、最も無駄で、最もユーザーの信頼を損なう瞬間はどれか。
それは、存在しないフォームを突然呼び出してしまい、「実行時エラー ‘2450’: Microsoft Access は、参照している [FormName] フォームを見つけられません。」という冷酷なダイアログが画面に鎮座した瞬間だ。

「いや、ボタンを押すんだからフォームはあるはずだ」
「設計時には確かに存在していた」

そんな開発者の甘い前提は、改修の嵐が吹き荒れる現場では容易に打ち砕かれる。他の開発者がフォームを削除したかもしれない。命名規則が変わったかもしれない。
エラーハンドリング(`On Error Resume Next`)でその場をしのごうとするアマチュアのコードは、重大な予期せぬバグまで握りつぶし、データベースを静かに腐敗させる。

プロのアーキテクトが目指すべきは、エラーを「捕捉する」ことではない。「エラーが発生する余地をコードの構造から排除する」ことだ。
今回は、`Application.CurrentProject.AllForms` コレクションを極限まで活用し、存在しないフォームへのアクセスを未然に完全に防ぐ、堅牢な防御的プログラミングの極意を伝授する。

なぜ「エラー処理での場当たり的な対応」は悪なのか

多くの初中級プログラマは、以下のようなコードを書く。

‘ 【アンチパターン】エラー処理に逃げた脆弱なコード
Sub Bad_OpenForm(formName As String)
On Error Resume Next ‘ エラーを無視する最悪の悪手
DoCmd.OpenForm formName
If Err.Number <> 0 Then
MsgBox “フォームがありません!”, vbCritical
End If
On Error GoTo 0
End Sub

このアプローチがなぜ実務で破綻するのか。
1. 意図しないエラーの隠蔽: `DoCmd.OpenForm` の中で発生した別のエラー(レコードソースの破損や変数の初期化ミスなど)まで握りつぶされ、デバッグが極めて困難になる。
2. パフォーマンスの劣化: エラー発生時の例外処理機構はVBAにおいて重い処理であり、これをルーチン化するとUIのレスポンスが体感できるレベルで悪化する。

プロが求めるのは、「実行する前に、そこにあることが確実である」という論理的証明である。それを最もエレガントに実現するのが `CurrentProject.AllForms` だ。

`CurrentProject.AllForms` の本質と正しい理解

`Application.CurrentProject.AllForms` は、データベース内に存在するすべてのフォームのメタデータ(`AccessObject`)を保持するコレクションだ。

ここで重要な設計上の注意点がある。
Accessのオブジェクトには、デザインビューで存在するものと、実際にメモリ上にロード(Open)されているものがある。

  • `CurrentForms` コレクション:現在ロードされているフォームのみを返す。
  • `CurrentProject.AllForms` コレクション:ロードの有無に関わらず、データベースに定義されているすべてのフォームを返す。

UIの存在確認を行う場合、私たちが知りたいのは「そのフォームが設計図として存在するかどうか」であるため、必ず後者の `CurrentProject.AllForms` を使用しなければならない。

【プロダクションコード】実務で即戦力となる堅牢なフォーム制御モジュール

ここに示すのは、実際のエンタープライズ環境(複数人開発・頻繁な改修が行われる現場)でもビクともしない、洗練されたラッパー関数の実装だ。

標準モジュールに記述し、プロジェクト全体の「UIの安全弁」として機能させる。

Option Compare Database
Option Explicit

‘ ==============================================================================
‘ módulo名: basFormController
‘ 概要: フォームの存在確認および安全な操作をカプセル化した防御的プログラミングモジュール
‘ ==============================================================================

/

  • 指定したフォームがデータベースに存在するかを厳密に判定する
  • @param {String} targetFormName – 確認したいフォーム名
  • @return {Boolean} 存在する場合はTrue、それ以外はFalse

/
Public Function IsFormExists(ByVal targetFormName As String) As Boolean
Dim frmObj As AccessObject

‘ 早期リターン:引数の不正チェック
If Trim(targetFormName) = “” Then
IsFormExists = False
Exit Function
End If

‘ AllForms コレクションを走査
‘ ※For Eachによる安全な列挙
For Each frmObj In Application.CurrentProject.AllForms
If StrComp(frmObj.Name, targetFormName, vbTextCompare) = 0 Then
IsFormExists = True
Exit Function
End If
Next frmObj

IsFormExists = False
End Function

/

  • 存在確認を行った上で、安全にフォームを開く(存在しない場合はユーザーに通知)
  • @param {String} formName – 開くフォーム名
  • @param {AcFormView} viewMode – 表示モード(省略時は通常フォームビュー)
  • @param {Variant} whereCondition – 抽出条件(省略可能)
  • @return {Boolean} 成功した場合はTrue、失敗した場合はFalse

/
Public Function SafeOpenForm( _
ByVal formName As String, _
Optional ByVal viewMode As AcFormView = acNormal, _
Optional ByVal whereCondition As Variant = Null) As Boolean

‘ 1. 存在しない場合は実行せず、ログを残してFalseを返す
If Not IsFormExists(formName) Then
MsgBox “指定されたフォーム [” & formName & “] はシステム内に存在しません。” & vbCrLf & _
“システム管理者または開発者にお問い合わせください。”, _
vbCritical + vbOKOnly, “フォーム不存在エラー”
SafeOpenForm = False
Exit Function
End If

‘ 2. ロード済みであっても安全に開く(DoCmdのラップ)
If IsNull(whereCondition) Then
DoCmd.OpenForm formName:=formName, View:=viewMode
Else
DoCmd.OpenForm formName:=formName, View:=viewMode, whereCondition:=whereCondition
End If

SafeOpenForm = True
End Function

この設計がもたらす圧倒的なメリット

1. 呼び出し側のコードが極限まで美しくなる

各画面のコマンドボタン等のイベントドリブンなコードは、以下のように記述するだけでよろしい。エラーハンドリングのノイズが完全に消え去る。

Private Sub cmdOpenCustomer_Click()
‘ 顧客管理フォームを安全に開く(存在チェックはSafeOpenForm内部で完結)
If Not SafeOpenForm(“frmCustomerDetail”, acNormal, “CustomerID = ” & Me.txtCustomerID) Then
‘ 開けなかった場合のフォールバック処理をここに書くことも可能
Exit Sub
End If
End Sub

2. 変更耐性の向上(保守性の飛躍的ジャンプ)

将来的にフォーム名が `frmCustomerDetail` から `frmCustomerMaster_v2` に変更されたとする。
ソースコード全体を `DoCmd.OpenForm` で散りばめていた場合、すべてのモジュールをしらみつぶしに修正する地獄が待っている。
しかし、このようなラッパー関数を介した設計にしておけば、定数管理や設定テーブルとの連携へスムーズに移行でき、変更箇所を最小限に抑えることができる。

アーキテクトからの最終提言

プロとアマチュアの決定的な違いは、「動けばいい」という妥協をするか、「破綻しない構造をデザインする」という執念を持つかだ。

Access VBAは手軽ゆえに、スパゲッティコードが量産されやすい。だからこそ、開発者であるあなたが境界線(Boundary)を厳格に引き、システムの外側からの不正な入力や、開発中の変更による構造の揺らぎを、コードの内側で完璧に受け止めなければならない。

`Application.CurrentProject.AllForms` を武器に、あなたのアプリケーションから「予期せぬ実行時エラー」を駆逐せよ。それこそが、真にプロフェッショナルな業務システム開発である。

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