1. はじめに:なぜコードの中に「設定値」を書いてはいけないのか?
Excel VBAでマクロを組み始めた頃、誰もが一度はやってしまう書き方があります。
‘ よくある失敗例:コードの中に直接パスや設定を書いている
Sub ExportReport()
Dim filePath As String
filePath = “C:\Users\yamada\Documents\MonthlyReport\Output” ‘ ← これ!
Dim maxRows As Long
maxRows = 5000 ‘ ← これも!
‘ 処理が続く…
End Sub
このように、コード内に直接フォルダパス、ファイル名、最大行数、接続先URLなどを記述することを「ハードコード(直書き)」と呼びます。
最初はこれで問題なく動きます。しかし、実務で運用を始めるとどうなるでしょうか?
- 「出力先フォルダが変わったので直してください」
- 「担当者が変わったのでパスを差し替えてください」
- 「テスト環境から本番環境へ切り替えます」
そのたびに、あなたはVBE(Visual Basic Editor)を開き、コードを検索し、修正して保存し直さなければなりません。もし、プログラムに詳しくない同僚にマクロを渡していたら? あなたが不在のときに設定変更が必要になったら?
ここで登場するのが「定数・設定の外部化」です。
プログラムの「処理ロジック」と「パラメータ(設定値)」を分離し、外部ファイル(INIファイルやJSONファイル)から読み込む構造を作ります。ここをクリアすれば、あなたのExcel VBAは「単なる自動化マクロ」から「誰もが安全に使える業務システム」へと劇的に進化しますよ!
—
2. 外部ファイル形式の選び方:INI vs JSON
設定を外部化する際、主に使われるフォーマットがINIファイルとJSONファイルです。どちらを採用すべきか、それぞれの特徴を整理しておきましょう。
【設定ファイルの比較】
◆ INIファイル(伝統的・シンプル)
[Database]
Server = 192.168.1.100
Port = 3306
└ メリット:Windows標準機能(API)だけで超高速に読める。
└ デメリット:ネスト(階層構造)が作れない。
◆ JSONファイル(モダン・構造的)
{
“Database”: {
“Server”: “192.168.1.100”,
“Port”: 3306
}
}
└ メリット:配列や階層構造を美しく表現できる。Web APIとも相性抜群。
└ デメリット:VBA標準にはJSONパーサーがないため、少し工夫が必要。
- シンプルなKey-Value(キーと値のペア)だけで十分な場合 $\rightarrow$ INIファイル
- 設定項目が多く、グループ化や配列を扱いたい場合 $\rightarrow$ JSONファイル
今回は、業務現場で即戦力として使える「INIファイル読み込み手法」と「JSON設定ファイルの読み込み手法」の両方を、極上の設計コード付きで解説します。
—
3. 実践1:Win32 APIを極める「INIファイル」読み込み
まずは最も信頼性が高く、動作が軽量なINIファイルの読み込み手法です。
Windowsに標準用意されている `GetPrivateProfileString` というAPIを使用します。
INIファイルの準備(config.ini)
Excelファイルと同じフォルダに、以下の内容で `config.ini` を作成してください(文字コードはANSI/Shift-JISで保存するのがVBAでのWin32 API利用時の鉄則です)。
[PathInfo]
OutputDir=C:\CompanyData\Output
LogDir=C:\CompanyData\Logs
[SystemConfig]
MaxRetryCount=3
TimeoutSeconds=60
DebugMode=True
VBA実装コード(標準モジュールに配置)
API宣言から、バッファの安全なトリミング、ファイルが存在しない場合の自動フォールバックまで考慮したプロ仕様のコードです。
Option Explicit
‘ — Win32 API 宣言 —
If Win64 Then
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
Else
Private Declare 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
End If
”’
”’
”’ セクション名(例: “PathInfo”)
”’ キー名(例: “OutputDir”)
”’ 取得失敗時のデフォルト値
”’ INIファイルのフルパス
”’
Public Function GetIniValue(ByVal section As String, _
ByVal key As String, _
Optional ByVal defaultValue As String = “”, _
Optional ByVal iniFilePath As String = “”) As String
‘ パスが省略された場合、ブックと同じフォルダの “config.ini” を参照
If iniFilePath = “” Then
iniFilePath = ThisWorkbook.Path & “\config.ini”
End If
‘ ファイル存在チェック
If Dir(iniFilePath) = “” Then
GetIniValue = defaultValue
Exit Function
End If
‘ APIに引き渡すための固定長バッファ(メモリ空間)を確保
Dim buffer As String
Dim bufferSize As Long
bufferSize = 1024
buffer = String$(bufferSize, vbNullChar)
‘ API呼び出し
Dim length As Long
length = GetPrivateProfileString(section, key, defaultValue, buffer, bufferSize, iniFilePath)
‘ ヌル文字(vbNullChar)を取り除いて文字だけを抽出
If length > 0 Then
GetIniValue = Left$(buffer, length)
Else
GetIniValue = defaultValue
End If
End Function
💡 先輩エンジニアの解説ポイント:なぜバッファ(`String$`)を用意するのか?
Win32 APIはC言語で作られています。C言語の世界では「文字を入れるためのメモリ箱」をあらかじめ呼び出し側(VBA側)で準備しておかなければなりません。
`buffer = String$(1024, vbNullChar)` で1024文字分の空箱を作り、APIに「ここに結果を書き込んでね」と渡しています。読み込み後、APIが返した文字数(`length`)分だけ `Left$` で切り出すことで、完璧な文字列を取り出せます。
—
4. 実践2:モダンで柔軟な「JSONファイル」読み込み
続いて、より複雑なデータ構造も扱えるJSONファイルの読み込み手法を解説します。
外部ライブラリ(DLL等)のインストールを一切不要にするため、`Scripting.FileSystemObject` で読み込み、VBA標準の `Scripting.Dictionary` 構造へ変換するアプローチを採用します。
JSONファイルの準備(config.json)
Excelと同じフォルダに `config.json`(UTF-8形式)を作成します。
{
“OutputDir”: “C:\\CompanyData\\Output”,
“LogDir”: “C:\\CompanyData\\Logs”,
“MaxRetryCount”: “3”,
“DebugMode”: “True”
}
VBA実装コード(軽量・自作JSONパーサー)
サードパーティ製のライブラリを使わなくても、設定値(1階層のKey-Value)程度であれば正規表現(`VBScript.RegExp`)を使って安全かつエレガントに解析可能です。
Option Explicit
”’
”’
Public Function LoadJsonConfig(Optional ByVal jsonPath As String = “”) As Object
Dim dict As Object
Set dict = CreateObject(“Scripting.Dictionary”)
dict.CompareMode = 1 ‘ 大文字・小文字を区別しない (vbTextCompare)
If jsonPath = “” Then
jsonPath = ThisWorkbook.Path & “\config.json”
End If
‘ ファイル非存在時は空のDictionaryを返す
Dim fso As Object
Set fso = CreateObject(“Scripting.FileSystemObject”)
If Not fso.FileExists(jsonPath) Then
Set LoadJsonConfig = dict
Exit Function
End If
‘ テキストファイルを全件読み込み
Dim stream As Object
Set stream = fso.OpenTextFile(jsonPath, 1, False) ‘ ForReading
Dim fileContent As String
fileContent = stream.ReadAll
stream.Close
‘ 正規表現を使って “キー”: “値” のペアを抽出
Dim regEx As Object
Set regEx = CreateObject(“VBScript.RegExp”)
With regEx
.Global = True
.IgnoreCase = True
.Pattern = “””([^””]+)””\s:\s””([^””]+)””” ‘ “Key”: “Value” パターン
End With
Dim matches As Object
Set matches = regEx.Execute(fileContent)
Dim match As Object
For Each match In matches
Dim k As String
Dim v As String
k = match.SubMatches(0)
v = match.SubMatches(1)
‘ JSONのエスケープシーケンス `\\` を `\` に復元
v = Replace(v, “\\”, “\”)
If Not dict.Exists(k) Then
dict.Add k, v
End If
Next match
Set LoadJsonConfig = dict
End Function
—
5. オブジェクトのライフサイクルとシングルトンパターンの極意
ここで、ワンランク上のアーキテクチャ設計についてお話しします。
設定ファイルを処理のルーチン内で「毎回ファイルを開いて読み込む」という実装をすると、パフォーマンスが著しく低下し、ディスクI/Oの無駄が発生します。
これを防ぐためのベストプラクティスが「シングルトン(初回のみ読み込み、メモリ保持)」パターンです。
究極の設定管理モジュール(`mod_Config.bas`)
Option Explicit
‘ モジュールレベル変数でキャッシュ(読み込み保持)する
Private m_ConfigCache As Object
”’
”’
Public Function AppConfig(ByVal key As String, Optional ByVal defaultValue As String = “”) As String
‘ キャッシュが空の場合のみファイルからロードする(初回1回のみ)
If m_ConfigCache Is Nothing Then
Set m_ConfigCache = LoadJsonConfig()
End If
‘ 指定したキーが存在すれば返し、無ければデフォルト値を返す
If m_ConfigCache.Exists(key) Then
AppConfig = m_ConfigCache(key)
Else
AppConfig = defaultValue
End If
End Function
”’
”’
Public Sub ReloadConfig()
Set m_ConfigCache = Nothing
End Sub
実際の業務コードでの呼び出し方
呼び出し側は、設定ファイルがINIなのかJSONなのか、どこにあるのかすら意識する必要がありません。
Public Sub MainProcess()
‘ 設定値の取得(初回だけファイル読み込みが発生し、2回目以降はメモリから一瞬で返る)
Dim outputFolder As String
outputFolder = AppConfig(“OutputDir”, “C:\DefaultOutput”)
Dim debugFlag As Boolean
debugFlag = CBool(AppConfig(“DebugMode”, “False”))
‘ — 実際のメイン処理 —
MsgBox “出力先フォルダ: ” & outputFolder, vbInformation, “処理開始”
If debugFlag Then
Debug.Print “【DEBUG】詳細ログを出力中…”
End If
End Sub
—
6. 陥りやすいエラーと回避テクニック
外部ファイル連携を導入した際に直面しやすいトラブルと、その解決策をまとめました。
【トラブル解決マッピング】
① ファイルが見つからない(Err 53 / 存在しないパス)
└ 原因:相対パスの誤解、またはネットワークドライブ未接続。
└ 対策:`ThisWorkbook.Path` を基準とし、存在しない場合は必ずデフォルト値を設定。
② 文字化けする(日本語が含まれる設定値)
└ 原因:文字コードのミスマッチ。
└ 対策:Win32 API(INI) = Shift-JIS (ANSI) で保存。
FileSystemObject(JSON/Text) = UTF-8またはShift-JISを明確に意識する。
③ 設定ファイルが開かれていてロックされる
└ 原因:VBA側で OpenTextFile / Stream を Close し忘れている。
└ 対策:エラー処理(On Error GoTo)を組み込み、Finally節で確実に Close する。
—
7. まとめ:マクロ作成者から「自動化ツールの設計者」へ
今回のポイントを整理しましょう。
1. ハードコードを破棄する:フォルダパスや定数はコード内に直接書かず、外部ファイルへ出す。
2. INI / JSONの使い分け:簡単な設定なら Win32 API の INI、拡張性重視なら JSON。
3. シングルトンで高速化:読み込みは初回1回のみ。モジュールレベル変数にキャッシュする。
4. フォールバック設計:ファイルが無かったりキーが無くても、デフォルト値で止まらず動く堅牢性を作る。
定数を外部化できるようになった瞬間、あなたの作成するツールは「自分しか触れないマクロ」から「誰に渡しても壊れない、現場で長く愛されるシステム」へと変化します。
ここをクリアできれば、Excel VBAの変数・定数・環境設計の基本はバッチリです! 自信を持って、洗練されたコードを書いていきましょう。
