Reputation: 22265
I'm curious if I'm missing something in understanding APIs that one should call first with NULL parameters to retrieve the required buffer size and then call them again once the buffer was allocated.
My understanding was that this buffer length will not change from the 1st call to the 2nd. (Well, assuming that we're not in the "race condition" situation, which is another story.)
So here's a simple real-life example that I'm observing on Windows XP SP2 (64-bit). The method below acquires current user's 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);
}
That part where I do dwSize == dwSize2
after the second call to GetTokenInformation
, well it fails because dwSize
is returned as 44
from the first call to GetTokenInformation
, and then dwSize2
is returned as 36
from the 2nd call.
Is this behavior normal?
Upvotes: 0
Views: 257
Reputation: 596226
It is not unheard of for an API to return a larger value when retrieving the needed byte size (maybe it has be rounded up for alignment purposes, etc), and then the same API to return a smaller value when retrieving the actual data, since it is reporting how many bytes were actually written into the allocated memory. You don't have to compare the two sizes, trust that if the function says it succeeded than it really did and that the written size is <= the allocated size.
If you are worried about a race condition that alters the needed byte size, use a loop to allocate the memory until successful:
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);
}
Upvotes: 2