JSON形式の設定ファイルから定数を読み込む:VBAでの外部設定管理の自動化
Excel VBAによるエンタープライズシステムの開発において、最も忌むべきアンチパターンの一つが「マジックナンバーや接続文字列のハードコーディング」である。仕様変更のたびにVBEを開き、ソースコードを修正してコンパイルし直す——この原始的なデプロイ手法は、バージョン管理の混乱やヒューマンエラーを誘発する温床となる。
真に堅牢なVBAアーキテクチャにおいては、「コードと設定の完全な分離」が絶対要件となる。本稿では、外部のJSONファイルから動的に設定値を読み込み、VBAの定数(あるいはそれに準ずる不変のコンテキスト)としてマウントする極限の設計手法を解説する。
レガシー環境の制約をいかに突破し、メモリ管理とパフォーマンスを最適化するか。現場の最前線で培った知見をここに開示する。
—
1. VBAにおける外部設定管理のアーキテクチャ選定
VBAで設定ファイルを扱う場合、伝統的なINIファイル(`GetPrivateProfileString` API利用)やXML、そして近年の標準であるJSONが選択肢に挙がる。
なぜ今、JSONなのか。それはデータ構造の表現力とメンテナンス性、そして将来的な他言語(PythonやC#など)との連携コストの低さにおいて、JSONが圧倒的な優位性を持つからである。
しかし、VBAにはネイティブのJSONパーサーが存在しない。ここでシニアエンジニアが直面するのが、以下の2つのアプローチの選択だ。
1. レガシーな `MSScriptControl.ScriptControl` の利用
- 32bit/64bit環境の差異やCOMコンポーネントのセキュリティ制約(IEコンポーネント依存)という大きな爆弾を抱える。
2. 純粋なVBA製JSONパーサー(VBA-JSON等)の利用
- 外部依存を減らせる一方で、巨大なJSONをパースする際のメモリ効率やガベージコレクションの挙動に配慮が必要。
今回は、環境依存を極力排除しつつ、COMのライフサイクルを完全に制御したモダンかつ堅牢な実装手法を提示する。
—
2. 実装:JSONパースと設定管理モジュールの構築
以下のコードは、外部の `config.json` を読み込み、アプリケーション全体で参照可能な設定コンテキストを構築するモジュール(`clsConfigManager` および標準モジュール)の実装例である。
設定ファイルの構造 (`config.json`)
{
“Environment”: “Production”,
“TimeoutSeconds”: 30,
“ApiEndpoint”: “https://api.example.com/v1/process”,
“DatabasePath”: “\\\\server\\share\\data\\master.accdb”
}
チーフアーキテクトによる実装コード
‘ =========================================================================
‘ クラスモジュール: clsConfigManager
‘ 役割: 外部JSONの読込、メモリ最適化、および設定値の安全なカプセル化
‘ =========================================================================
Option Explicit
Private m_Config As Object ‘ ScriptControl または Dictionaryを格納
Private m_IsLoaded As Boolean
‘ イニシャライザー
Private Sub Class_Initialize()
m_IsLoaded = False
End Sub
‘ ターミネイター:COMオブジェクトの参照リークを確実に防ぐ
Private Sub Class_Terminate()
Call ClearMemory
End Sub
‘ 外部JSONファイルを読み込み、メモリ上に展開する
Public Function LoadConfig(ByVal filePath As String) As Boolean
On Error GoTo ErrorHandler
Dim fso As Object
Dim ts As Object
Dim jsonText As String
Dim sc As Object
‘ 既にロード済みの場合は一度クリア
If m_IsLoaded Then Call ClearMemory
‘ ファイル存在確認
Set fso = CreateObject(“Scripting.FileSystemObject”)
If Not fso.FileExists(filePath) Then
Err.Raise 53, “ConfigManager”, “設定ファイルが見つかりません: ” & filePath
End If
‘ UTF-8等に対応したテキスト読み込み
Set ts = fso.OpenTextFile(filePath, 1, False, -2) ‘ -2 = システムデフォルト(TristateUseDefault)
jsonText = ts.ReadAll
ts.Close
‘ ScriptControlによるJSON評価 (JScriptエンジンを利用)
‘ ※64bit環境の制約やセキュリティパッチに注意が必要な場合はVBA-JSONへ換装を推奨
Set sc = CreateObject(“MSScriptControl.ScriptControl”)
sc.Language = “JScript”
‘ グローバルスコープにJSONオブジェクトを評価させるためのラッパー
sc.AddCode “function parseJSON(jsonStr) { return eval(‘(‘ + jsonStr + ‘)’); }”
Set m_Config = sc.Run(“parseJSON”, jsonText)
m_IsLoaded = True
LoadConfig = True
GoTo Cleanup
ErrorHandler:
m_IsLoaded = False
LoadConfig = False
MsgBox “設定ファイルの読み込みに失敗しました: ” & Err.Description, vbCritical, “致命的なエラー”
Cleanup:
‘ 参照の明示的解放(メモリリークの根絶)
On Error Resume Next
If Not ts Is Nothing Then ts.Close
Set ts = Nothing
Set fso = Nothing
Set sc = Nothing
On Error GoTo 0
End Function
‘ プロパティ:環境名を取得
Public Property Get Environment() As String
If Not m_IsLoaded Then Err.Raise 91, , “設定がロードされていません。”
Environment = CStr(m_Config.Environment)
End Property
‘ プロパティ:タイムアウト秒数を取得
Public Property Get TimeoutSeconds() As Long
If Not m_IsLoaded Then Err.Raise 91, , “設定がロードされていません。”
TimeoutSeconds = CLng(m_Config.TimeoutSeconds)
End Property
‘ プロパティ:APIエンドポイントを取得
Public Property Get ApiEndpoint() As String
If Not m_IsLoaded Then Err.Raise 91, , “設定がロードされていません。”
ApiEndpoint = CStr(m_Config.ApiEndpoint)
End Property
‘ プロパティ:DBパスを取得
Public Property Get DatabasePath() As String
If Not m_IsLoaded Then Err.Raise 91, , “設定がロードされていません。”
DatabasePath = CStr(m_Config.DatabasePath)
End Property
‘ メモリ最適化とオブジェクトの強制解放
Private Sub ClearMemory()
On Error Resume Next
If Not m_Config Is Nothing Then
Set m_Config = Nothing
End If
m_IsLoaded = False
On Error GoTo 0
End Sub
—
3. シニアエンジニアが知るべき「メモリ最適化」と「COMのライフサイクル」
VBAにおける最大の罠は、「見えないCOMオブジェクトの残骸」である。特に `ScriptControl` や `Scripting.FileSystemObject` のような外部コンポーネントをループ内や場当たり的なエラーハンドリングの中で生成・破棄し続けると、VBAランタイムのメモリリークを引き起こし、最悪の場合Excelプロセス自体がクラッシュする。
1. 参照の即時切断(`Set … = Nothing` の作法)
VBAのガベージコレクションは参照カウント方式(Reference Counting)を採用している。オブジェクト変数がスコープを抜ける、あるいは明示的に `Nothing` が代入されることでカウントが0になりメモリが解放される。
しかし、エラー発生時にジャンプ(`On Error GoTo`)すると、変数のスコープ管理が狂い、参照が残ったままになるケースがある。上記のコードのように、`Cleanup` ラベルを用意し、例外発生時であっても確実にオブジェクトを破棄する構造が不可欠である。
2. シングルトンパターンによるインスタンスの寿命管理
設定ファイルは、アプリケーションのライフサイクル中において不変(あるいは一度読み込んだらセッション中は固定)であるべきだ。したがって、この `clsConfigManager` は毎回インスタンス化するのではなく、プロセス全体で単一のインスタンス(シングルトン)として保持するのが最も効率的である。
標準モジュール側に以下のようなアクセサを用意する。
‘ =========================================================================
‘ 標準モジュール: modAppContext
‘ 役割: グローバルな設定コンテキストの保持
‘ =========================================================================
Option Explicit
Private g_ConfigManager As clsConfigManager
Public Function Config() As clsConfigManager
If g_ConfigManager Is Nothing Then
Set g_ConfigManager = New clsConfigManager
‘ ブックと同じ階層の config.json を想定
Dim configPath As String
configPath = ThisWorkbook.Path & “\config.json”
If Not g_ConfigManager.LoadConfig(configPath) Then
Set g_ConfigManager = Nothing
End ‘ 致命的エラー時は即座にマクロを終了
End If
End If
Set Config = g_ConfigManager
End Function
‘ アプリケーション終了時のクリーンアップ
Public Sub TerminateApp()
If Not g_ConfigManager Is Nothing Then
Set g_ConfigManager = Nothing
End If
End Sub
これにより、コードのどの箇所からでも `Config.ApiEndpoint` のようにスマートかつ高速に設定値へアクセスできる。値はクラスのプロパティ経由で返されるため、実質的に「安全な定数」として機能する。
—
4. レガシー環境・セキュリティ制約への処方箋
現代のWindows環境(特にセキュリティが厳格化された企業内PC)において、`MSScriptControl.ScriptControl` はJScriptエンジンを内包しているため、グループポリシーやアンチウイルスソフトによってブロックされるリスクがある。
もし組織のセキュリティポリシーが `ScriptControl` の使用を許さない場合、シニアアーキテクトは以下の代替案を即座に選択できなければならない。
1. VBA-JSON (VBA専用JSONパーサー) への移行
- 外部COMに依存せず、純粋なVBAコード(Classモジュール)で文字列操作のみでパースを行うため、セキュリティ上のブロックを回避できる。ただし、極端に巨大なJSON(数MB以上)をパースする際のアルゴリズムの計算量($O(N^2)$ 等の罠)には注意が必要であり、正規表現の乱用によるパフォーマンス低下をプロファイルする必要がある。
2. Windows API (`GetPrivateProfileString`) へのフォールバック
- JSONの階層構造(ネスト)が必要ないシンプルなキーバリューストアであれば、レガシーに回帰してINIファイルをWindows API経由で高速に読み込む方が、実効パフォーマンスと安定性の面で勝る場合がある。
—
5. 総括
コードと設定の分離は、プロフェッショナルなシステム開発の第一歩に過ぎない。しかし、VBAという閉じた、かつ制約の多いエコシステムにおいてこれを徹底するためには、COMのメモリライフサイクルの理解と、堅牢なエラーハンドリング、そして環境制約を見据えたアーキテクチャの選択が求められる。
マジックナンバーをコードから駆逐し、外部JSONによる動的コンテキスト管理を導入せよ。それこそが、保守性に優れ、変化に耐えうる真のエンタープライズVBAソリューションである。
