トップ回答者
Windows Vista での GetUserName() について

質問
-
Windows Vista で WIN32API の GetUserName() を使用すると以下の条件において不可解な現象が発生します。
1.プログラムが setup.exe や uninst.exe などの Vista が自動的に昇格させると思われる名前になっている。
2.そのプログラムが使用する dll の DLL_PROCESS_ATTACH で GetUserName() を呼び出している。
3.そのプログラム内で GetUserName() を呼び出す。
この条件がそろった場合、3.において Secur32.dll でアクセス違反が発生してしまいます。プログラム名を test.exe などに変更すると問題は発生しません。また、UAC は有効、無効にかかわらず発生します。
なにか回避方法はあるでしょうか?
回答
すべての返信
-
matuno さんからの引用 1.プログラムが setup.exe や uninst.exe などの Vista が自動的に昇格させると思われる名前になっている。
...
プログラム名を test.exe などに変更すると問題は発生しません。
試してはいませんが、UAC のインストーラ検出関連で何かあるのかもしれませんね。
ちなみにアプリケーションマニフェストは使用していますか?
アプリケーションマニフェストで、
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
を指定すると、ファイル名が setup.exe 等でもインストーラとして検出されなくなりますので、ファイル名を test.exe に変更した場合と同じ条件になるかもしれません。
ちなみに、アプリケーションマニフェストについては、下記のスレッドや
http://forums.microsoft.com/MSDN-JA/ShowPost.aspx?PostID=1338495&SiteID=7
手前みそですが、下記を参考にしてみてください。
-
chack - Akira Inoue さんからの引用 ちなみにアプリケーションマニフェストは使用していますか?
アプリケーションマニフェストで、
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
を指定すると、ファイル名が setup.exe 等でもインストーラとして検出されなくなりますので、ファイル名を test.exe に変更した場合と同じ条件になるかもしれません。
回答ありがとうございます。
setup.exe.manifest を作成して、requestedExecutionLevel を全てのレベルで試してみましたが結果は同じでした。
どうしても回避できないのであれば、プログラムの名前を変えようと思いますが、一応再現する C のサンプルコードを記載しておきます。
// DLL のソースコード =========================
#include <windows.h>
#include <stdio.h>
#define USERNAME_SIZE 64
static TCHAR _UserName[USERNAME_SIZE];
__declspec(dllexport) DWORD QueryUserName( TCHAR *Name, DWORD Size, BOOL APICalled )
{
DWORD result = 0;
if (APICalled) {
if (GetUserName(Name, &Size))
result = Size;
} else {
strcpy(Name, _UserName);
result = strlen(_UserName);
}
return result;
}
BOOL WINAPI DllMain ( HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpv )
{
switch(dwReason) {
case DLL_PROCESS_ATTACH:
QueryUserName(_UserName, USERNAME_SIZE, TRUE);
break;
}
return TRUE;
}
// ホストアプリケーションのソースコード ====================
#include <windows.h>
#include <stdio.h>
__declspec(dllexport) DWORD QueryUserName( TCHAR *Name, DWORD Size, BOOL APICalled );
#define USERNAME_SIZE 64
void main(int argc, char *argv[])
{
TCHAR name[USERNAME_SIZE];
if (QueryUserName(name, USERNAME_SIZE, TRUE) > 0)
printf("%s\n", name);
}
-
ちょっと試してみましたが、確かに Vista ではエラーになりますね。
で、回避策ですが、
(1) ファイル名に Setup などの名前が含まれていても、UAC で権限昇格が不要な場合は、埋め込みマニフェストで
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
を指定して、権限昇格しなければ回避できます(確認済み)。
setup.exe.manifest ファイルを作成して外部マニフェストで試されたようですが、Vista の場合、外部マニフェストファイルだと上手く認識してくれない場合があります。
VS2005でしたら、プロジェクトのプロパティの「マニフェストツール」-「入力と出力」で「追加のマニフェストファイル」に requestedExecutionLevel を記述したマニフェストファイルを指定することで、簡単に埋め込みマニフェストとしてリソースに含めることができます。
(2) DllMain の DLL_PROCESS_ATTACH での QueryUserName 関数の呼び出し(GetUserName API の呼び出し)を行わないように実装を変更すれば回避できます(確認済み)。
switch(dwReason) {
case DLL_PROCESS_ATTACH:
//QueryUserName(_UserName, USERNAME_SIZE, TRUE); <--- これをやめる
break;}
例えば、DLL に
__declspec(dllexport) void Init()
{
QueryUserName(_UserName, USERNAME_SIZE, TRUE);
}
のような API を用意して、以下のようにアプリケーション側から呼び出すように変更します。
void main(int argc, char *argv[])
{
Init();
TCHAR name[USERNAME_SIZE];
if (QueryUserName(name, USERNAME_SIZE, TRUE) > 0)
printf("%s\n", name);
}
上記いずれかの方法で試されてみてはいかがでしょうか。