iooiau.net

GetPrivateProfileString() の罠

INI ファイルから文字列の設定を読み込むための GetPrivateProfileString() という API 関数がありますが、これを普通に使うと陥る罠があります。
GetPrivateProfileString() は、文字列が " か ' で囲まれている場合、それを除去します。
しかし、この動作がまずい結果を生む場合があります。

例えば、INI ファイルに以下のような項目があるとします。


[Settings]
Command="C:\Program Files\Hoge\Hoge.exe" "%1"

これを GetPrivateProfileString() で読み込んだとします。


TCHAR szCommand[MAX_PATH];
GetPrivateProfileString(
    TEXT("Settings"), TEXT("Command"), NULL,
    szCommand, sizeof(szCommand) / sizeof(TCHAR), szFileName);

すると、読み込まれた文字列は以下のようになってしまいます。


C:\Program Files\Hoge\Hoge.exe" "%1

これでは、この設定を実際に使う時にまずいことになってしまいます。

対策としては、WritePrivateProfileString() で書き出す際に、全体を余分に " で囲っておけば良いでしょう。
例えば C 言語で書くなら以下のようにします。


BOOL WriteIniString(LPCTSTR pszSection, LPCTSTR pszKeyName, LPCTSTR pszString, LPCTSTR pszFileName)
{
    if (pszString[0] == _T('"') || pszString[0] == _T('\'')) {
        int Length = lstrlen(pszString);
        LPTSTR pszBuffer = (LPTSTR)malloc((Length + 3) * sizeof(TCHAR));
        BOOL Result;

        if (pszBuffer == NULL)
            return FALSE;
        pszBuffer[0] = _T('"');
        lstrcpy(pszBuffer + 1, pszString);
        pszBuffer[Length + 1] = _T('"');
        pszBuffer[Length + 2] = _T('\0');
        Result = WritePrivateProfileString(pszSection, pszKeyName, pszBuffer, pszFileName);
        free(pszBuffer);
        return Result;
    }
    return WritePrivateProfileString(pszSection, pszKeyName, pszString, pszFileName);
}

C++ なら std::basic_string なんかを使えば大分簡潔に書けますね。


BOOL WriteIniString(LPCTSTR pszSection, LPCTSTR pszKeyName, LPCTSTR pszString, LPCTSTR pszFileName)
{
    if (pszString[0] == _T('"') || pszString[0] == _T('\'')) {
        std::basic_string<TCHAR> Buffer;
        Buffer = _T('"');
        Buffer += pszString;
        Buffer += _T('"');
        return WritePrivateProfileString(pszSection, pszKeyName, Buffer.c_str(), pszFileName);
    }
    return WritePrivateProfileString(pszSection, pszKeyName, pszString, pszFileName);
}

同様の問題として、文字列の先頭か末尾に空白文字がある場合も、GetPrivateProfileString() で読み込んだ際に除去されてしまいます。
もしそれも維持したい場合は、その判定も加えるといいでしょう。

この問題は、そもそも WritePrivateProfileString() と GetPrivateProfileString() とで対応がとれていないことにあるのですが、限りなくバグに近い仕様ですね。