C++ Windows XP 64位上GetTokenInformation的意外行为

C++ Windows XP 64位上GetTokenInformation的意外行为,c++,windows,winapi,C++,Windows,Winapi,我很好奇,在理解API时是否遗漏了一些东西,应该首先使用空参数调用这些API来检索所需的缓冲区大小,然后在分配缓冲区后再次调用它们 我的理解是,缓冲区长度不会从第一次调用更改为第二次调用。(好吧,假设我们没有处于“竞争条件”的情况,这是另一回事。) 下面是我在WindowsXPSP2(64位)上观察到的一个简单的现实例子。以下方法获取当前用户的SID: HANDLE hToken; if(OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &

我很好奇,在理解API时是否遗漏了一些东西,应该首先使用空参数调用这些API来检索所需的缓冲区大小,然后在分配缓冲区后再次调用它们

我的理解是,缓冲区长度不会从第一次调用更改为第二次调用。(好吧,假设我们没有处于“竞争条件”的情况,这是另一回事。)

下面是我在WindowsXPSP2(64位)上观察到的一个简单的现实例子。以下方法获取当前用户的SID:

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);
}