【Access VBA極限知見】フォーム間連携の悪習を断つ:OpenArgs×JSONで実現するスマートパラメータ制御
業務システムの開発現場で、こんなコードを見たことはないだろうか。
`DoCmd.OpenForm` の引数にカンマ区切りの文字列を詰め込み、受け取る側で `Split` 関数を乱れ打ちして配列のインデックスをハードコーディングする――。
‘ 【反面教師】絶対にやってはいけない保守性ゼロの密結合コード
‘ 送信側
DoCmd.OpenForm “F_Detail”, , , , , , “123,山田太郎,1”
‘ 受信側(F_DetailのForm_Load)
Dim v As Variant
v = Split(Me.OpenArgs, “,”)
Me.txtID = v(0)
Me.txtName = v(1)
Me.txtType = v(2)
この実装方法がなぜ非効率で危険なのか。理由は明白だ。後から「担当者コード」や「フラグ」の項目が追加・変更された瞬間、すべてのインデックスがズレてシステム全体が沈没する。マジックナンバーの嵐であり、仕様変更に対する耐性が皆無な「技術的負債」そのものだ。
今回は、Access VBAの `OpenArgs` に JSON文字列 を流し込み、画面間の複雑なパラメータ制御をキー名ベースで一括管理する、プロダクション品質の極限テクニックを伝授する。
—
1. なぜ「OpenArgs × JSON」なのか?
`OpenArgs` は、Variant型(実質的にString型)のデータを1つだけ別フォームへ持ち運べる唯一のパイプラインだ。ここに構造化データであるJSONを載せることで、以下の圧倒的なメリットが手に入る。
1. 完全な疎結合: パラメータの順番に依存しない。キー名でバインドするため、項目の追加・削除にビクともしない。
2. 拡張性の担保: 単なるIDだけでなく、初期表示モード(新規/編集/参照)やコールバック用プロシージャ名など、任意のメタデータを無限に同梱できる。
3. 可読性の向上: デバッグ時に `Debug.Print Me.OpenArgs` を叩くだけで、渡されたコンテキストが丸裸になり、一目で把握できる。
しかし、ここでAccess開発者なら誰もがこう思うはずだ。
「おい、VBAには標準でJSONパーサーがないじゃないか」 と。
安心してほしい。今回は、VBA環境で外部ライブラリ(JScriptやVBA-JSONなど)をインポートする手間を排除し、正規表現(VBScript.RegExp)を用いて、Access標準機能だけで超高速かつ堅牢にJSONのキー値を取り出す軽量パーサーを実装する。
—
2. 【実装】プロダクションコード
以下のコードは、エラーハンドリングを完備し、実際の業務システムでそのままコピー&ペーストして即座に使える実用モジュールだ。
① フォームを呼び出す側(送信側の極意)
送信側では、Dictionaryや自前の文字列結合、あるいは簡易的なJSONビルダーを使って文字列を組み立て、`OpenArgs` に乗せる。
‘ ==============================================================================
‘ 呼び出し元フォーム (例: 検索画面) のイベント
‘ ==============================================================================
Private Sub cmdOpenDetail_Click()
On Error GoTo ErrorHandler
Dim jsonArgs As String
Dim targetID As Long
‘ 選択されているレコードのIDを取得(例)
If Me.ListResults.ListIndex = -1 Then
MsgBox “対象レコードが選択されていません。”, vbExclamation, “警告”
Exit Sub
End If
targetID = Me.ListResults.Column(0)
‘ JSON形式のペイロードを組み立てる
‘ ※実務ではエスケープ処理を考慮すること。数値やbooleanはダブルクォーテーション不要。
jsonArgs = “{” & _
“””Action””: “”Edit””,” & _
“””TargetID””:” & targetID & “,” & _
“””UserRole””: “”Administrator””,” & _
“””CallerForm””: “”” & Me.Name & “””” & _
“}”
‘ OpenArgsにJSONを搭載してフォームを開く
DoCmd.OpenForm “F_ClientDetail”, _
View:=acNormal, _
WindowMode:=acDialog, _
OpenArgs:=jsonArgs
‘ 画面が閉じられた後の後続処理
Me.Requery
SafeExit:
Exit Sub
ErrorHandler:
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical, “システムエラー”
Resume SafeExit
End Sub
② パラメータを受け取る側(受信側フォームの極意)
受け取る側(`F_ClientDetail`)の `Form_Load` イベントでJSONをパースし、フォームの状態を制御する。
‘ ==============================================================================
‘ 呼び出し先フォーム (例: 詳細画面 F_ClientDetail) のモジュール
‘ ==============================================================================
Private Sub Form_Load()
On Error GoTo ErrorHandler
Dim rawArgs As String
Dim actionType As String
Dim clientID As Long
Dim userRole As String
rawArgs = Nz(Me.OpenArgs, “”)
‘ OpenArgsが空の場合は不正起動とみなして弾く
If rawArgs = “” Then
MsgBox “パラメータが不正です。直接起動はできません。”, vbCritical, “致命的エラー”
DoCmd.Close acForm, Me.Name
Exit Sub
End If
‘ ————————————————————————–
‘ JSONから値抽出(正規表現によるキー名抽出)
‘ ————————————————————————–
actionType = GetJsonValue(rawArgs, “Action”)
CLng(GetJsonValue(rawArgs, “TargetID”)) ‘ 数値変換テストを兼ねる
clientID = Val(GetJsonValue(rawArgs, “TargetID”))
userRole = GetJsonValue(rawArgs, “UserRole”)
‘ ————————————————————————–
‘ パラメータに基づく画面のコンテキスト制御
‘ ————————————————————————–
Select Case actionType
Case “Edit”
Me.Caption = “クライアント情報 編集 (ID: ” & clientID & “)”
Me.AllowAdditions = False
‘ レコードの検索・バインド処理
Me.RecordSource = “SELECT FROM T_Clients WHERE ClientID = ” & clientID
Case “New”
Me.Caption = “クライアント情報 新規作成”
Me.AllowEdits = True
DoCmd.GoToRecord , , acNewRec
Case Else
MsgBox “未知のアクションです: ” & actionType, vbCritical, “エラー”
DoCmd.Close acForm, Me.Name
Exit Sub
End Select
‘ 権限に応じたUI制御
If userRole <> “Administrator” Then
Me.cmdDeleteRecord.Enabled = False
End If
SafeExit:
Exit Sub
ErrorHandler:
MsgBox “フォームの初期化に失敗しました: ” & Err.Description, vbCritical, “エラー”
DoCmd.Close acForm, Me.Name
Resume SafeExit
End Sub
③ コアエンジン:VBA標準の軽量JSONパーサー(共通モジュールに配置)
外部依存ゼロ、VBA標準の `VBScript.RegExp` を用いて、指定したキーの値(文字列・数値)を高速に抽出するプロシージャだ。
‘ ==============================================================================
‘ 共通標準モジュール (例: modJsonParser)
‘ ==============================================================================
Option Explicit
‘
‘
Public Function GetJsonValue(ByVal json As String, ByVal key As String) As String
On Error GoTo ErrorHandler
Dim regEx As Object
Dim matches As Object
Dim pattern As String
Set regEx = CreateObject(“VBScript.RegExp”)
‘ 正規表現パターン: “Key” \s : \s ( “([^”\\](\\.[^”\\]))” | ([^,\}]) )
‘ 文字列値と数値・Booleanを同時にキャッチする堅牢なパターン
pattern = “””” & key & “””\s:\s(?:””([^””])””|([^,^}]))”
With regEx
.Global = False
.IgnoreCase = True
.MultiLine = True
.Pattern = pattern
End With
If regEx.Test(json) Then
Set matches = regEx.Execute(json)
‘ グループ1(ダブルクォーテーションで囲まれた文字列)か、グループ2(数値等)を返す
If matches(0).SubMatches(0) <> “” Then
GetJsonValue = matches(0).SubMatches(0)
Else
GetJsonValue = Trim(matches(0).SubMatches(1))
End If
Else
GetJsonValue = “” ‘ キーが存在しない場合
End If
SafeExit:
Exit Function
ErrorHandler:
GetJsonValue = “”
End Function
—
3. 現場で絶対にハマる「落とし穴」と回避策
このアーキテクチャを現場に投入する際、チーフアーキテクトとして以下の罠への対策を強く推奨する。
1. 文字列値の中にダブルクォーテーション(`”`)やカンマが含まれるリスク
手動でJSON文字列を組み立てる場合、ユーザーの入力値に `”` が混ざるとJSONの構文が破壊される。
- 対策: 本格的な運用に乗せる場合は、VBA-JSON等の信頼できるライブラリのDictionaryシリアライザを使うか、値に渡す文字列のエスケープ関数(例: `Replace(str, “”””, “\”””)`)を必ず通すこと。上記の正規表現パーサーはフラット構造(ネストしていないJSON)に特化させることで、バグの温床を排除している。
2. `acDialog` 以外のモードで開いた際のライフサイクル
`DoCmd.OpenForm` を `acDialog` 以外(モードレス等)で開く場合、呼び出し元と呼び出し先が非同期で動くため、OpenArgsのデータの寿命やタイミングに注意が必要だ。
- 対策: 複雑なパラメータ制御や結果のフィードバックを伴う画面遷移では、原則として `WindowMode:=acDialog` を強制し、ダイアログパターンで設計せよ。これが最もバグを生み出さない王道である。
—
総括
「動けばいいや」という雑な文字列結合による `OpenArgs` の運用は、システムの寿命を確実に縮める。
キー名ベースのJSON構造化パースを取り入れることで、「誰が・どの画面から・どんなコンテキストでそのフォームを叩いたか」がコードの意図として明確になり、保守性は劇的に跳ね上がる。
プロフェッショナルたるもの、細部まで美しく、拡張性に満ちたアーキテクチャを設計しよう。あなたの書くコードが、次のメンテナンス担当者を救う最高のドキュメントになるのだから。
