VBAで設定ファイルを扱いたいとき、INIファイルは最もシンプルで扱いやすい形式の一つです。本記事では、Win32 APIのGetPrivateProfileString関数を使ってINIファイルから設定値を取得する方法を、実践的なサンプルコードとともに解説します。
INIファイルとは
INIファイルは、Windowsアプリケーションの設定を保存するためのテキストベースのファイル形式です。Microsoftは現在、設定の保存にレジストリやXMLファイルの使用を推奨していますが、そのシンプルさから今でも多くの開発現場で利用されています。
VBAでも外部の設定ファイルを利用したい場合があります。その場合に利用される1つの方法がINIファイルです。
INIファイルの基本構造
WindowsにおけるINIファイルは一般的に以下のような構成をします。INIファイルは文字コードがShift-JISで書かれたテキストファイルです。
| 1 2 3 | ; セミコロン以降はコメント [Section] key=value | 
主なルールは次の通りです。
- 文字コードはShift-JIS:
 GetPrivateProfileString関数を利用する場合は、INIファイルの文字コードはShift-JISである必要があります。文字コードがUTF-8など場合は設定値に全角文字が含まれている場合、取得した値が文字化けを起こします。
- セクション名は[]で囲む:
 セクション(Section)の定義には[]で囲まれている必要があります。
- セクションの有効範囲:
 次のSectionが定義されるまでのkeyとvalueは直前のSectionに所属しているとみなされます。
- キー名の重複:
 Sectionが異なっていればkey名は重複しても構いません。
- 末尾の半角スペース:
 valueの値が半角スペースで終わっている場合、後述するGetPrivateProfileString関数で取得した場合、半角スペースを除いた値を取得します。
INIファイルの例
以下は、TCP通信設定を保存するINIファイルの例です。INIファイルの内容を変更することで、アプリケーションを変更せずに設定を変えることができます。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 | ;//------------------------------------------ ;// ファイル名:tcp.ini ;// 内容      :TCP処理に必要な設定を保持する ;//------------------------------------------ ;// TCPサーバ情報 [TCP_SERVER] IP=192.168.20.1 PORT=12345 ;// TCPクライアント情報 [TCP_CLIENT] IP=192.168.11.5 PORT=50005 | 
指定キーの値をGetPrivateProfileString関数で取得する
通常、テキストファイルの内容を取得するにはFileSystemObjectクラスやOpenTextメソッドを利用して、1行ずつ内容を判定しながら取得する、という流れになりますが、上記のINIファイルの形式を守っている場合はWin32APIのGetPrivateProfileString関数を利用することでファイル操作の処理を行わずに指定したキーの値を取得することが可能です。
GetPrivateProfileString関数の機能
GetPrivateProfileString関数はINIファイル内で第一引数のlpApplicationNameパラメータで指定されたセクション以降にある第二引数のlpKeyNameパラメータで指定された名前に一致するキーを検索します。
キーが見つかった場合、そのキーに対応する文字列を第四引数に取得値として設定します。
キーが見つからなかった場合は第三引数のlpDefaultパラメータで指定された既定の文字列を第四引数の取得値として設定します。
構文は以下の通りです。Declareステートメント等はサンプルコードをご参照ください。
| 1 2 3 4 5 6 7 8 | Function GetPrivateProfileString( _     ByVal lpApplicationName As String, _     ByVal lpKeyName As Any, _     ByVal lpDefault As String, _     ByVal lpReturnedString As String, _     ByVal nSize As Long, _     ByVal lpFileName As String ) As Long | 
| lpApplicationName | セクション文字列を指定します。 | 
| lpKeyName | キー文字列を指定します。 | 
| lpDefault | キー文字列が見つからなかった場合の代替値を文字列で指定します。 | 
| lpReturnedString | キー文字列に対応する値が設定されます。 事前に値の文字列が格納できるサイズまで拡張しておく必要があります。 値の文字列長の方が大きい場合は、事前に拡張したサイズまでしか取得できません。 | 
| nSize | lpReturnedStringパラメータのサイズを指定します。 | 
| lpFileName | INIファイルのパスを指定します。 | 
| 戻り値 | 取得した文字列のバイト数をShift-JIS換算(半角は1、全角は2として)返します。 | 
サンプルコード
上記のtcp.iniファイルから値を取得するサンプルです。
GetPrivateProfileString関数を使う場合、Declareステートメントで宣言しておく必要があります。
取得値を保持する変数は事前に拡張しておく必要があるため、Space関数で拡張しています。
取得後はTrimだけでは完全に半角スペースを除去できないため、本来の値だけを取得したい場合はNULL文字がある直前までを取得する必要があります。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | Declare PtrSafe Function GetPrivateProfileString Lib _     "kernel32" Alias "GetPrivateProfileStringA" ( _     ByVal lpApplicationName As String, _     ByVal lpKeyName As Any, _     ByVal lpDefault As String, _     ByVal lpReturnedString As String, _     ByVal nSize As Long, _     ByVal lpFileName As String _ ) As Long Sub GetPrivateProfileStringTest()     Dim sPath                   '// INIファイルのパス     Dim sValue      As String   '// 取得値     Dim lSize       As Long     '// 取得値のサイズ     Dim lRet        As Long     '// 戻り値     lSize = 20     sPath = "C:\test\tcp.ini"     '// 取得バッファを初期化     sValue = Space(lSize)     '// [TCP_SERVER]セクションの"IP"キーの値を取得     lRet = GetPrivateProfileString("TCP_SERVER", "IP", "non", sValue, lSize, sPath)     Debug.Print "[" & sValue & "]"     Debug.Print "[" & Trim(sValue) & "]"     Debug.Print "[" & Trim(Left(sValue, InStr(sValue, Chr(0)) - 1)) & "]"     '// 取得バッファを初期化     sValue = Space(lSize)     '// [TCP_CLIENT]セクションの"PORT"キーの値を取得     lRet = GetPrivateProfileString("TCP_CLIENT", "PORT", "non", sValue, lSize, sPath)     Debug.Print "[" & sValue & "]"     Debug.Print "[" & Trim(sValue) & "]"     Debug.Print "[" & Trim(Left(sValue, InStr(sValue, Chr(0)) - 1)) & "]" End Sub | 
実行結果
| 1 2 3 4 5 6 7 8 | [192.168.20.1        ] [192.168.20.1 ] [192.168.20.1] [192.168.20.1] [50005               ] [50005 ] [50005] [50005] | 
取得値から値部分だけを抽出するには
上のサンプルコードでも書いていますが、取得値はそのままではNULL文字や空白スペースなどのゴミが含まれています。
そこで、NULL文字以降を除去するために、Instr関数でNull文字の位置を検索し、Left関数でそこまでを取得するようにします。
「Trim(Left(sValue, InStr(sValue, Chr(0)) – 1))」のように書きます。
GetPrivateProfileString関数の戻り値が値のバイト数であることを利用して、「Left(sValue, lRet)」のように書くことも出来ますが、戻り値は文字数ではなくバイト数のため、値が半角文字だけであれば正しく取得できますが、全角文字が含まれている場合は不要部分を全て除去できません。
GetPrivateProfileString関数の注意点
サンプルコードの説明でも少し書きましたが、GetPrivateProfileString関数には癖というか注意点がいくつかあります。
- API宣言が必要
 他のWin32APIも同様に、Declare宣言が必要です。ただ、これは他のWin32APIも同様のため、そこまで大変な話ではありません。
- バッファの事前確保
 取得用の文字列変数は、Space関数などで事前にサイズを確保する必要があります。通常のVBAの処理ではこのようなコードを書くことはほとんどないため、特殊な処理になります。事前に拡張しておく必要があるのと、取得後に不要な部分を除去しなければならないという手間が掛かります。
- パフォーマンスの問題
 最も重要な注意点ですが、処理が遅いという注意点があります。関数を呼び出しているだけですので目には見えませんが、GetPrivateProfileString関数を実行するたびにファイルのオープンとクローズが発生するため、取得する値が多くなればなるほど、遅延の原因になります。
- 文字コードの制限
 INIファイルの文字コードがShift-JISに限定されます。最近ではUTF-8の利用が増えていため、この制限がコード管理上、問題になることがあります。
このような癖を考慮して使う必要があります。
より高速な取得方法
多数の設定値を読み込む必要がある場合や、パフォーマンスを重視する場合は、FileSystemObjectを使って一度にファイル全体を読み込み、Dictionary等で管理する方法をお勧めします。
その方法のコードの詳細は「VBAで高速にINIファイルから値を取得する」をご参照ください。
まとめ
GetPrivateProfileString関数は、シンプルなINIファイル読み込みには便利ですが、制限事項も多い関数です。プロジェクトの要件に応じて、適切な実装方法を選択してください。
この関数が適している場合:
- 読み込む設定項目が少ない(5〜10項目程度)
- Shift-JISのINIファイルを扱う
- シンプルな実装を優先したい
別の方法を検討すべき場合:
- 大量の設定値を読み込む
- UTF-8など他の文字コードを使いたい
- パフォーマンスが重要