C++ Windows XP 64位上GetTokenInformation的意外行为
我很好奇,在理解API时是否遗漏了一些东西,应该首先使用空参数调用这些API来检索所需的缓冲区大小,然后在分配缓冲区后再次调用它们 我的理解是,缓冲区长度不会从第一次调用更改为第二次调用。(好吧,假设我们没有处于“竞争条件”的情况,这是另一回事。) 下面是我在WindowsXPSP2(64位)上观察到的一个简单的现实例子。以下方法获取当前用户的SID:C++ Windows XP 64位上GetTokenInformation的意外行为,c++,windows,winapi,C++,Windows,Winapi,我很好奇,在理解API时是否遗漏了一些东西,应该首先使用空参数调用这些API来检索所需的缓冲区大小,然后在分配缓冲区后再次调用它们 我的理解是,缓冲区长度不会从第一次调用更改为第二次调用。(好吧,假设我们没有处于“竞争条件”的情况,这是另一回事。) 下面是我在WindowsXPSP2(64位)上观察到的一个简单的现实例子。以下方法获取当前用户的SID: HANDLE hToken; if(OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &
HANDLE hToken;
if(OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &hToken))
{
DWORD dwSize = 0;
if(!GetTokenInformation(hToken, TokenUser, NULL, dwSize, &dwSize) &&
::GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{
BYTE* pb = new (std::nothrow) BYTE[dwSize];
if(pb)
{
TOKEN_USER* pTU = (TOKEN_USER*)pb;
DWORD dwSize2;
if(GetTokenInformation(hToken, TokenUser, pTU, dwSize, &dwSize2) &&
dwSize == dwSize2)
{
LPWSTR pName;
if(ConvertSidToStringSid(pTU->User.Sid, &pName))
{
//Got it!
_tprintf(L"User SID=%s\n", pName);
LocalFree(pName);
}
}
delete[] pb;
}
}
CloseHandle(hToken);
}
在第二次调用GetTokenInformation
后,我执行的dwSize==dwSize2
部分失败,因为dwSize
从第一次调用GetTokenInformation
返回为44
,然后从第二次调用返回为dwSize2
这种行为正常吗?只要dwSize大于dwSize2,它就应该正常。只要dwSize大于dwSize2,它就应该正常。只要dwSize大于dwSize2,它就应该正常。只要dwSize大于dwSize2,应该没问题。API在检索所需的字节大小时返回较大的值(可能为了对齐等目的已将其四舍五入),然后在检索实际数据时返回较小的值,这并非闻所未闻,因为它报告实际写入分配内存的字节数。您不必比较这两个大小,请相信,如果函数说它比实际成功,并且写入的大小是,那么API在检索所需字节大小时返回更大的值(可能是为了对齐等目的而对其进行了四舍五入),然后使用相同的API在检索实际数据时返回较小的值,因为它报告实际写入分配内存的字节数。您不必比较这两个大小,请相信,如果函数说它比实际成功,并且写入的大小是,那么API在检索所需字节大小时返回更大的值(可能是为了对齐等目的而对其进行了四舍五入),然后使用相同的API在检索实际数据时返回较小的值,因为它报告实际写入分配内存的字节数。您不必比较这两个大小,请相信,如果函数说它比实际成功,并且写入的大小是,那么API在检索所需字节大小时返回更大的值(可能是为了对齐等目的而对其进行了四舍五入),然后使用相同的API在检索实际数据时返回较小的值,因为它报告实际写入分配内存的字节数。你不必比较这两种大小,相信如果函数说它比实际成功了,那么写出来的大小是感谢解释的。我只是不知道API可以在第一次调用中存储一些额外的字节。至于比赛条件,不幸的是,你的循环想法不会消除它。一般来说,那些双重调用API是一个可怕的想法。不管是谁提出了这个概念,都需要对他/她的大脑进行检查。对于用户SID,它可能是OK,但是如果没有它尖叫“race condition!”@c0000fd,则无法调用诸如
enumprocess
之类的API:在最坏的情况下,循环可能必须运行多次,但大小不能无限增加,因此迟早会退出。感谢您的解释。我只是不知道API可以在第一次调用中存储一些额外的字节。至于比赛条件,不幸的是,你的循环想法不会消除它。一般来说,那些双重调用API是一个可怕的想法。不管是谁提出了这个概念,都需要对他/她的大脑进行检查。对于用户SID,它可能是OK,但是如果没有它尖叫“race condition!”@c0000fd,则无法调用诸如enumprocess
之类的API:在最坏的情况下,循环可能必须运行多次,但大小不能无限增加,因此迟早会退出。感谢您的解释。我只是不知道API可以在第一次调用中存储一些额外的字节。至于比赛条件,不幸的是,你的循环想法不会消除它。一般来说,那些双重调用API是一个可怕的想法。不管是谁提出了这个概念,都需要对他/她的大脑进行检查。对于用户SID,它可能是OK,但是如果没有它尖叫“race condition!”@c0000fd,则无法调用诸如enumprocess
之类的API:在最坏的情况下,循环可能必须运行多次,但大小不能无限增加,因此迟早会退出。感谢您的解释。我只是不知道API可以在第一次调用中存储一些额外的字节。至于比赛条件,不幸的是,你的循环想法不会消除它。一般来说,那些双重调用API是一个可怕的想法。不管是谁提出了这个概念,都需要对他/她的大脑进行检查。对于用户SID,它可能是OK,但是如果没有它尖叫“race condition!”@c0000fd,则无法调用诸如enumprocess
之类的API:在最坏的情况下,循环可能必须运行多次,但大小不能无限增加,因此它迟早会退出。
HANDLE hToken;
if (OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &hToken))
{
BYTE* pb = NULL;
DWORD dwSize = 0;
TOKEN_USER* pTU = NULL;
BOOL bRet;
do
{
bRet = GetTokenInformation(hToken, TokenUser, pTU, dwSize, &dwSize);
if (bRet)
break;
if (::GetLastError() != ERROR_INSUFFICIENT_BUFFER)
break;
delete[] pb;
pb = new (std::nothrow) BYTE[dwSize];
if (!pb)
break;
pTU = (TOKEN_USER*)pb;
}
while (true);
if (bRet)
{
// use pTU as needed...
}
delete[] pb;
CloseHandle(hToken);
}