это типичный шаблон winapi - api должен возвращать некоторую информацию в буфер памяти.но вместо этого выделите буфер самостоятельно - это заставит вызывающего абонента выделить буфер.поэтому вызывающая сторона должна выделить сам буфер и передать ему указатель и размер в API.API проверяет размер буфера - если он достаточно большой, чтобы заполнить информацию для буфера, в противном случае возвращается ERROR_INSUFFICIENT_BUFFER
(предположим, что больше нет ошибок) или иногда ERROR_MORE_DATA
.которая, какая конкретная ошибка возвращалась ERROR_INSUFFICIENT_BUFFER
или ERROR_MORE_DATA
, как правило, прямо задокументировано для вызова API.различаются между этими 2 ошибками: ERROR_INSUFFICIENT_BUFFER
- означает, что никакая информация не заполнена в буфере вообще, когда ERROR_MORE_DATA
означает, что некоторые данные возвращаются, но не завершены.и api возвращает пользователю, через некоторый параметр out, требуемый размер буфера в этом случае.часто это делается через один и тот же параметр inout - указатель на DWORD.во входных данных указывает размер выделенного пользователем буфера, в выходных - указывает требуемый размер буфера или размер возвращаемых данных
часто, какой размер буфера требуется - неизвестно в начале.поэтому нам нужно либо вызвать api с буферами размера 0, либо выделить несколько якобы достаточного размера буфера.если буфера будет недостаточно - перераспределите или расширите его и снова вызовите api.для некоторых API (например, CredUnPackAuthenticationBufferW
) требуемый выходной буфер не изменяется со временем (если входные параметры не изменяются), но обычный размер выходного буфера может изменяться между вызовами - даже второй вызов с размером буфера, возвращенным первым вызовом, может завершиться ошибкой с буферомошибка размера (потому что возвращаемые данные могут расти между вызовами).в этом случае нужно вызвать api в цикле do/while(error == ERROR_INSUFFICIENT_BUFFER/ERROR_MORE_DATA)
.но даже в том случае, если выходной буфер не изменяется со временем, лучше сделать это циклом с одним вызовом API внутри, а не с 2 вызовами API.
для конкретного кода дела может выглядеть как
ULONG cred()
{
CREDUI_INFO ci = { sizeof(ci) };
BOOL bSave = FALSE;
PVOID pvOutAuthBuffer;
ULONG ulOutAuthBufferSize;
ULONG ulAuthPackage = 0;
ULONG dwError = CredUIPromptForWindowsCredentials(
&ci, NOERROR, &ulAuthPackage, 0, 0,
&pvOutAuthBuffer, &ulOutAuthBufferSize,
&bSave, CREDUIWIN_ENUMERATE_ADMINS );
if (dwError == NOERROR)
{
ULONG cchUserName = 0;
ULONG cchPassword = 0;
ULONG cchDomain = 0;
static volatile UCHAR guz = 0;
PWSTR stack = (PWSTR)alloca(guz);
PWSTR szUserName = 0, szPassword = 0, szDomainName = 0;
ULONG cchNeed, cchAllocated = 0;
do
{
if (cchAllocated < (cchNeed = cchUserName + cchPassword + cchDomain))
{
szUserName = (PWSTR)alloca((cchNeed - cchAllocated) * sizeof(WCHAR));
cchAllocated = (ULONG)(stack - szUserName);
szPassword = szUserName + cchUserName;
szDomainName = szPassword + cchPassword;
}
dwError = CredUnPackAuthenticationBuffer(
CRED_PACK_PROTECTED_CREDENTIALS,
pvOutAuthBuffer, ulOutAuthBufferSize,
szUserName, &cchUserName,
szDomainName, &cchDomain,
szPassword, &cchPassword)
? NOERROR : GetLastError();
if (dwError == NOERROR)
{
DbgPrint("%S@%S %S\n", szDomainName, szUserName, szPassword);
break;
}
} while (dwError == ERROR_INSUFFICIENT_BUFFER);
CoTaskMemFree(pvOutAuthBuffer);
}
return dwError;
}