2012年1月9日月曜日

ExpandEnvironmentStrings の問題

自分のための備忘録です。

Windows7 と XP で ExpandEnvironmentStrings の挙動が違うもよう。
基本的に XP での動作が怪しい感じ。

ExpandEnvironmentStrings についてはこんな感じ。
DWORD ExpandEnvironmentStrings(
LPCTSTR lpSrc, // 環境変数を表す文字列へのポインタ
LPTSTR lpDst, // 展開後の環境変数を表す文字列へのポインタ
DWORD nSize // 展開後の文字列の最大文字数
);

戻り値
関数が成功すると、展開後の文字列を受け取るバッファに格納された文字数が返ります。
指定したバッファのサイズより展開後の文字列数の方が大きいときは、展開後の文字列を
保持するために必要なバッファのサイズが返ります。


日本語を含む文字列の展開
char* hoge()
{
    DWORD len = ExpandEnvironmentStringsA( "%USERPROFILE%\\デスクトップ", NULL, 0 );
    char* str = new char [len];
    ExpandEnvironmentStringsA( "%USERPROFILE%\\デスクトップ", str, len );
    return str;
}
1回目の ExpandEnvironmentStringsA で必要なサイズ(文字数)を取得して、
2回目の ExpandEnvironmentStringsA 動的確保したバッファに読み込みます。

使い方に関して、一見問題なさそうですが、
Windows XP では、len に想定外の値が返ってきます。
実際に必要な文字数より大きい値であれば、必要な文字列が取得できるのですが(余分なメモリは確保されてしまうが)小さい値になる場合もあるようで、この場合文字列が切り捨てられてしまいます。

対策としては、余分にバッファを確保するか、ExpandEnvironmentStringsW を使うといったところでしょうか…

非常に長い文字列の展開
日本語を含む文字列の展開については、ExpandEnvironmentStringsW を使うことにして対処しました。
しかし、ExpandEnvironmentStringsW にも問題がありました。
TEST(EnvironmentStrings, LongLongString)
{
    wchar_t str[0x10000];
    // 適当な文字列を作る
    for( int i=0; i < 0x10000; ++i )
    {
        str[i] = L'0' + i%10;
    }
    str[0x10000-1] = L'\0';
    DWORD len = ExpandEnvironmentStringsW( str, NULL, 0 );
    ASSERT_EQ( 0x10000, len );
}
上記コードは、Windows7 では成功します。 しかし、Windows XP では len に 0x7FFF が返ってきて、失敗してしまいます。 どうやら、XP では 0x8000 以上の文字列は展開できないようです… そんなに長い文字列を展開することがあるのか、という気もしますが、 最近あったからこれを書いているわけで… 対策としては、事前に分解して長い文字列の展開をしないように工夫するしかないですね…
追記
  • 2012/01/22
    TEST(EnvironmentStrings, LongLongString) のコードに間違いがあったので修正しました。

0 件のコメント:

コメントを投稿