Шаблоны для вызова функций Win32 API, которые возвращают ERROR_MORE_DATA или ERROR_INSUFFICIENT_BUFFER? - PullRequest
2 голосов
/ 09 июля 2009

Функции в Win32 SDK, которые возвращают данные переменного размера, обычно позволяют пропускать буфер нулевого размера, а затем сообщают вам, сколько места вам понадобится. Они имеют тенденцию выглядеть как одно из следующих (упрощенное):

LSTATUS RegQueryValueExA (HKEY hKey, LPCSTR lpValueName,
                          LPDWORD lpReserved, LPDWORD lpType,
                          LPBYTE lpData, LPDWORD lpcbData);

Наивная попытка назвать это может выглядеть так:

DWORD dwType;
DWORD cbData = 0;
LSTATUS status = RegQueryValueEx(hKey, "InstallFolder",
                                 NULL, &dwType,
                                 NULL, &cbData);

BYTE *pData = (BYTE *)malloc(cb);
status = RegQueryValueEx(hKey, "InstallFolder",
                                 NULL, &dwType,
                                 pData, &cbData);

// Do something with pData

Другие функции выглядят следующим образом:

BOOL SetupDiGetClassDescriptionA(CONST GUID *ClassGuid,
                                 PSTR ClassDescription,
                                 DWORD ClassDescriptionSize,
                                 PDWORD RequiredSize);

Мой вопрос: есть ли шаблоны / шаблоны для надежной обработки такого рода функций? Например, если память фактически заканчивается, или размер изменяется между двумя вызовами?

Ответы [ 2 ]

3 голосов
/ 09 июля 2009

Я предпочитаю не отправлять NULL с первой попытки. Вместо этого я передаю локально определенный буфер, размер которого достаточно велик для большинства случаев. Будучи объявленным локально, вы не тратите время на выделение ресурсов, и единственные издержки - это вероятность исчерпания стека (что невелико). Требуемый размер возвращается, даже если вы передаете реальный буфер, которого недостаточно, но только в этом случае вам придется выделить буфер из кучи. Таким образом, вы обязательно в конечном итоге отправите достаточно большой буфер, и в большинстве случаев вам придется вызывать функцию только один раз и без динамического выделения.

Например (не проверено):

DWORD dwType;
DWORD cbData = SOME_SIZE;
BYTE *pData, pStackBuffer[SOME_SIZE];
bool bFreeData = false;

pData = pStackBuffer;
LSTATUS status = RegQueryValueEx(hKey, "InstallFolder", NULL, &dwType,
                                 pData, &cbData);
if (status == ERROR_MORE_DATA)
{
    pData = (BYTE*)malloc(cbData);
    bFreeData = true;
    status = RegQueryValueEx(hKey, "InstallFolder", NULL, &dwType,
                             pData, &cbData);
}

// do the stuff...

if (bFreeData)
    free(pData);
1 голос
/ 10 июля 2009

Я не согласен с тем, что угадывание со стеком буфера является лучшим подходом. почти во всех случаях упрощение кода при передаче значения NULL превосходит незначительное влияние на производительность: двойной вызов API и выделение небольшого объема памяти в куче.

таким образом, вам не нужно беспокоиться о более сложной обработке ошибок и логике alloc / free. Вы также избегаете потенциальных проблем безопасности.

при этом в представленном примере использование MAX_PATH и однократный вызов RegQueryValueEx, вероятно, является лучшим вариантом.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...