こんにちは! Access VBAの海原へようこそ。
マクロの記録ボタンを押すだけの世界から一歩踏み出し、自分の手でロジックを組み立てる楽しさに目覚めたあなたへ。今回は、現場のプロが必ず押さえている「エラーを未然に防ぐスマートな防御壁」のお話です。
ここをクリアすれば、あなたの書くプログラムの「格」が一段と上がります。一緒にマスターしていきましょう!
—
なぜ、存在しないレポートを呼び出すと世界が崩壊するのか?
AccessでVBAを書いていると、ボタン一つでレポートを印刷したり、プレビューしたりする処理をいたるところで書きたくなりますよね。
例えば、こんなコードを書いたとします。
‘ 【危険なアンチパターン】
DoCmd.OpenReport “Rpt_MonthlySales”, acViewPreview
一見、なんの問題もない美しいコードに見えます。しかし、もし何かの拍子に「Rpt_MonthlySales」という名前のレポートが削除されていたり、名前が変更されていたりしたらどうなるでしょうか?
VBAの実行は容赦なく中断され、あの冷酷なエラーダイアログが画面に飛び込んできます。
> 実行時エラー ‘2450’:
> Microsoft Accessは、式の中で参照されている「Rpt_MonthlySales」という名前のフォームを見つけられません(※レポートの場合も同様のエラーが発生します)。
ユーザーがこのエラー画面に遭遇した瞬間、アプリの信頼性は地に落ちます。「なんだこのバグだらけのシステムは!」と。
私たち開発者の使命は、エラーを出さないことではありません。「エラーが起きる条件を予測し、優しくいなすこと」です。
—
救世主:`CurrentProject.AllReports` コレクション
ここで登場するのが、今回の主役である `Application.CurrentProject.AllReports`(通常は `CurrentProject.AllReports` と省略して書きます)です。
これは、「現在開いているデータベースに存在しているすべてのレポートの設計図(オブジェクト)」が詰まったリストボックスのようなものだと思ってください。
このコレクション(集まり)の何が素晴らしいかと言うと、「そのレポートが本当に存在するかどうか」を、実際に開く前にこっそりチェックできる点にあります。
図解イメージ
[ Access データベース ]
┗ CurrentProject.AllReports (すべてのレポートのリスト)
┣ “Rpt_CustomerList” (ある)
┣ “Rpt_ProductList” (ある)
┗ “Rpt_MonthlySales” (★ない!この時どうする?)
いきなり「開け!」と命令するのではなく、まず「おい、リストの中に『Rpt_MonthlySales』はあるか?」と点呼を取る。これがプロの技です。
—
実践! 存在チェックを組み込んだ堅牢なレポート印刷コード
百聞は一見に如かず。実際に開発現場でそのまま使える、極めて安全性の高いコードを見てみましょう。
以下のコードを、フォームのボタンクリックイベントなどに貼り付けてみてください。
Option Compare Database
Option Explicit
Private Sub cmdPrintReport_Click()
‘ 1. 定数定義(マジックナンバーやハードコーディングを避けるプロの作法)
Const TARGET_REPORT As String = “Rpt_MonthlySales”
On Error GoTo ErrorHandler
‘ 2. AllReportsコレクションを使って、レポートの存在を動的チェック
If Not IsReportExists(TARGET_REPORT) = True Then
‘ レポートが存在しない場合の優しいリカバリー処理
MsgBox “指定されたレポート ‘” & TARGET_REPORT & “‘ が見つかりません。” & vbCrLf & _
“システム管理者にお問い合わせください。”, _
vbExclamation, “レポート出力エラー”
Exit Sub
End If
‘ 3. 存在することが確実なので、安心してプレビューを開く
DoCmd.OpenReport TARGET_REPORT, acViewPreview
Exit Sub
ErrorHandler:
‘ 予期せぬその他のエラーをキャッチ
MsgBox “予期せぬエラーが発生しました。” & vbCrLf & _
“エラー番号: ” & Err.Number & vbCrLf & _
“エラー内容: ” & Err.Description, _
vbCritical, “システムエラー”
End Sub
汚
‘ =================================================================
‘ 補助関数: 指定したレポートが存在するかを真偽値(Boolean)で返す
‘ =================================================================
Private Function IsReportExists(ByVal reportName As String) As Boolean
Dim obj As AccessObject
‘ デフォルトは「存在しない」と仮定
IsReportExists = False
‘ AllReportsコレクションを総当たりでチェック
For Each obj In CurrentProject.AllReports
If obj.Name = reportName Then
IsReportExists = True
Exit Function ‘ 見つかったら即座にループを抜け出す(パフォーマンス最適化)
End If
Next obj
End Function
—
コードの深掘りポイント
ここが分かれば、Accessオブジェクトモデルの基本はバッチリです!
1. `For Each … In` によるコレクション走査
`CurrentProject.AllReports` の中身を一つずつ取り出して、変数 `obj` に代入しています。オブジェクト指向の基本であり、AccessVBAでは頻出のイディオムです。
2. `Exit Function` による高速化
目的のレポート名が見つかった瞬間にループを強制終了させています。数千個のオブジェクトを持つ巨大なシステムであっても、無駄な処理をさせないエンジニアの優しさ(こだわり)がここにあります。
3. 定数の活用 (`Const`)
レポート名をコード内に直接ベタ書き(ハードコーディング)せず、最初に `Const` で定義しています。これにより、将来レポート名が変わったときも、書き換える箇所が1カ所で済みます。
—
まとめ:ワンランク上のエンジニアへ
今回は `CurrentProject.AllReports` を使って、存在しないレポートへの参照エラーをスマートに回避する方法を解説しました。
マクロの記録から一歩進んだ私たちに必要なのは、「動けばいいや」というコードではなく、「どう転んでも壊れない、使い手に優しい」コードです。
このテクニックは、レポートだけでなく、フォームを扱う `CurrentProject.AllForms` でも全く同じように使えます。ぜひ、あなたのアプリのあちこちに応用してみてくださいね。
それでは、次回の極限の知見でお会いしましょう。開発を楽しんで!
