【テクニカル・上級編】定数管理の外部化:設定ファイル(INI/JSON)から定数を読み込む手法 – Excel VBA解析バイブル

スポンサーリンク

ハードコードの呪縛からの解放:外部設定ファイル(INI/JSON)によるVBA定数管理の極意

VBAのコードベースにおいて、接続先サーバーのIPアドレス、APIのエンドポイント、タイムアウト値、あるいは出力先のパスがソースコード内に直書き(ハードコード)されている光景を、私は幾度となく目撃してきた。

環境が変わるたびにVBEを開き、ソースコードを書き換えてコンパイルし、配布し直す――。これはエンジニアリングの冒涜であり、保守性の欠如そのものである。近代的なソフトウェアアーキテクチャにおいて、設定値の外部化は必須要件だ。VBAというレガシーな枠組みの中でも、設計思想を正しく持てば、モダンなアプリケーションと同等の柔軟性を手に入れることができる。

本稿では、Windows標準のINIファイル、および昨今のシステム間連携のデファクトスタンダードであるJSONファイルをVBAから駆使し、パフォーマンスと堅牢性を両立させた設定値の外部管理手法を徹底解説する。

1. なぜVBAで「設定の外部化」が必要なのか

VBAの実行環境は、多くの場合エンドユーザーのローカルPCというブラックボックスだ。ここに環境依存値を埋め込むことのリスクは計り知れない。

  • デプロイコストの削減: 接続先変更のたびに `.xlsm` を再配布・再インポートする必要がなくなる。
  • 関心の分離 (SoC): 「ビジネスロジック」と「環境依存パラメータ」を完全に分離し、変更に強いコードベースを構築する。
  • 権限の分離: エンドユーザーにロジックを触らせず、設定ファイルのみを管理者権限やスクリプトで制御可能にする。

VBAでこれを実現するためには、Windows API(Profile関数群)によるINIファイルの高速読み込みと、VBA標準のコレクション構造・標準モジュールを組み合わせたキャッシュ戦略が極めて有効である。

2. Windows APIを活用した超高速INIファイル管理

古き良きINIファイルは、OS標準のAPI(`GetPrivateProfileString` / `WritePrivateProfileString`)を利用できるため、外部ライブラリを一切必要としない。VBAとの親和性は依然として最高峰だ。

アーキテクチャの設計

設定値は毎回ファイルI/Oを行うとパフォーマンスが著しく低下する。そのため、「初回アクセス時にメモリ(Dictionary等)にロードし、セッション中はそれを参照する(遅延ロードとキャッシュ)」というライフサイクル管理を実装する。

実装コード(標準モジュール: `ModIniManager.bas`)

Option Explicit

‘ Win32 API declarations for INI file access
Private Declare PtrSafe Function GetPrivateProfileString Lib “kernel32” Alias “GetPrivateProfileStringA” ( _
ByVal lpAppName As String, _
ByVal lpKeyName As String, _
ByVal lpDefault As String, _
ByVal lpReturnedString As String, _
ByVal nSize As Long, _
ByVal lpFileName As String) As Long

Private Declare PtrSafe Function WritePrivateProfileString Lib “kernel32” Alias “WritePrivateProfileStringA” ( _
ByVal lpAppName As String, _
ByVal lpKeyName As String, _
ByVal lpString As String, _
ByVal lpFileName As String) As Long

Private m_ConfigCache As Object ‘ Scripting.Dictionary
Private m_IniPath As String

‘ =========================================================================
‘ 初期化と設定値の取得(キャッシュ機構付き)
‘ =========================================================================
Public Function GetConfig(ByVal Section As String, ByVal Key As String, Optional ByVal DefaultValue As String = “”) As String
If m_ConfigCache is Nothing Then
Call InitializeCache
End If

Dim cacheKey As String
cacheKey = UCase(Section) & “.” & UCase(Key)

If m_ConfigCache.Exists(cacheKey) Then
GetConfig = m_ConfigCache(cacheKey)
Else
‘ キャッシュミス時のフォールバックおよびINIからの直接取得
Dim retVal As String
retVal = ReadIniValue(Section, Key, DefaultValue)
m_ConfigCache(cacheKey) = retVal
GetConfig = retVal
End If
End Function

Private Sub InitializeCache()
Set m_ConfigCache = CreateObject(“Scripting.Dictionary”)
‘ ワークブックと同階層の “config.ini” をデフォルトパスとする
m_IniPath = ThisWorkbook.Path & “\config.ini”

If Dir(m_IniPath) = “” Then
‘ 設定ファイルが存在しない場合のハンドリング
Call WriteLog(“警告: config.iniが見つかりません。デフォルト値で動作します。”)
End If
End Sub

Private Function ReadIniValue(ByVal Section As String, ByVal Key As String, ByVal DefaultValue As String) As String
Dim buffer As String
Dim retSize As Long

buffer = Space$(255)
retSize = GetPrivateProfileString(Section, Key, DefaultValue, buffer, Len(buffer), m_IniPath)

If retSize > 0 Then
ReadIniValue = Left$(buffer, retSize)
Else
ReadIniValue = DefaultValue
End If
End Function

‘ 設定値の動的書き換え(管理ツール等からの利用を想定)
Public Sub SetConfig(ByVal Section As String, ByVal Key As String, ByVal Value As String)
If m_IniPath = “” Then m_IniPath = ThisWorkbook.Path & “\config.ini”

Dim result As Long
result = WritePrivateProfileString(Section, Key, Value, m_IniPath)

If result = 0 Then
Err.Raise 9999, “IniManager”, “設定ファイルの書き込みに失敗しました。”
End If

‘ キャッシュの更新
If Not m_ConfigCache Is Nothing Then
m_ConfigCache(UCase(Section) & “.” & UCase(Key)) = Value
End If
End Sub

3. システム間連携の要:JSON設定ファイルのパース

モダンなAPIやマイクロサービスとの連携において、設定ファイルにJSONを採用することは自然な選択肢だ。しかし、VBAにはネイティブのJSONパーサーが存在しない。

ここで外部の巨大なJSONライブラリを闇雲に持ち込むのは、メモリリークやバージョン競合のリスク(DLL Hell)を生むため避けるべきだ。軽量かつ堅牢なVBA製JSONパーサー(VBA-JSONなど)をモジュールとして直接組み込むのが、プロフェッショナルの選択である。

実装コード(JSON設定の読み込み例)

以下のコードは、一般的なVBA用JSONパーサー(例: `JsonConverter.bas`)がプロジェクト内に存在することを前提としている。

Option Explicit

Private m_JsonConfig As Object

‘ =========================================================================
‘ JSON設定ファイルのロードとメモリ展開
‘ =========================================================================
Public Sub LoadJsonConfig(Optional ByVal FilePath As String = “”)
If FilePath = “” Then
FilePath = ThisWorkbook.Path & “\config.json”
End If

If Dir(FilePath) = “” Then
Err.Raise 53, “JsonConfigManager”, “JSON設定ファイルが存在しません: ” & FilePath
End If

Dim fileNo As Integer
fileNo = FreeFile

Dim jsonText As String
Open FilePath For Input As #fileNo
jsonText = Input$(LOF(fileNo), fileNo)
Close #fileNo

‘ JsonConverter (VBA-JSON) を使用してDictionary/Collection階層に変換
‘ ※事前に JsonConverter.bas のインポートが必要
On Error GoTo ParseError
Set m_JsonConfig = JsonConverter.ParseJson(jsonText)
Exit Sub

ParseError:
Err.Raise 10001, “JsonConfigManager”, “JSONのパースに失敗しました。構文を確認してください: ” & Err.Description
End Sub

‘ ドット区切り(例: “Database.Server.Host”)で階層化された設定値を取得
Public Function GetJsonValue(ByVal KeyPath As String) As Variant
If m_JsonConfig Is Nothing Then
LoadJsonConfig
End If

Dim keys() As String
keys = Split(KeyPath, “.”)

Dim currentObj As Variant
Set currentObj = m_JsonConfig

Dim i As Long
For i = LBound(keys) To UBound(keys)
If IsObject(currentObj) Then
If TypeName(currentObj) = “Dictionary” Then
If currentObj.Exists(keys(i)) Then
Set currentObj = currentObj(keys(i))
If Not IsObject(currentObj) Then
‘ プリミティブ値に到達
If i = UBound(keys) Then
GetJsonValue = currentObj
Exit Function
End If
End If
Else
Err.Raise 10002, “JsonConfigManager”, “指定されたキーが見つかりません: ” & KeyPath
End If
End If
End If
Next i

Set GetJsonValue = currentObj
End Function

4. プロフェッショナルのメモリ管理とオブジェクトライフサイクル

VBAにおいて、`Object` 型や `Dictionary`、COMコンポーネントを扱う際の最大の罠は「暗黙的なメモリリーク」である。特にグローバル変数やモジュールレベルの変数にオブジェクトを保持し続ける場合、インスタンスの解放漏れがExcelプロセスの肥大化を招く。

チーフアーキテクトからの戒め:参照の明示的破棄

アプリケーション終了時、あるいはアドインのアンロード時には、確実にメモリ上のキャッシュを破棄しなさい。

‘ =========================================================================
‘ アプリケーション終了時やセッション終了時に必ず呼び出すクリーンアップ処理
‘ =========================================================================
Public Sub TerminateConfigManager()
On Error Resume Next

‘ INIキャッシュの解放
If Not m_ConfigCache Is Nothing Then
m_ConfigCache.RemoveAll
Set m_ConfigCache = Nothing
End If

‘ JSON設定オブジェクトの解放
If Not m_JsonConfig Is Nothing Then
‘ Dictionaryの場合は再帰的にクリアするか、ルートをNothingにする
Set m_JsonConfig = Nothing
End If

Debug.Print “ConfigManager: メモリ上の設定キャッシュを完全に解放しました。”
End Sub

この `TerminateConfigManager` を、`ThisWorkbook` の `Workbook_BeforeClose` イベントや、エラーハンドリングの最終段(`Finally` ブロック的運用)で確実に実行する設計に組み込むこと。これがプロとアマを分ける決定的な境界線だ。

5. まとめ:レガシーの皮を被ったモダンアーキテクチャの構築

VBAは、その古い歴史ゆえに「場当たり的なコードを書く言語」と揶揄されがちだ。しかし、それは言語の限界ではなく、書き手の設計思想の限界に他ならない。

今回解説した「設定の外部化」と「厳格なライフサイクル管理」を導入することで、あなたのVBAアプリケーションは、単なるマクロの集合体から、保守性が高く、エンタープライズ環境に耐えうる堅牢なシステムへと昇華する。

ハードコードの呪縛を断ち切り、変化に強いコードベースをその手で築き上げよ。

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