用于调用返回ERROR_MORE_DATA或ERROR_INSUFFICIENT_BUFFER的Win32 API函数的模式?

时间:2009-07-09 09:31:37

标签: winapi

返回可变大小数据的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 个答案:

答案 0 :(得分:3)

我不想在第一次尝试时发送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 :(得分:1)

我不同意使用堆栈缓冲区进行猜测是最好的方法。在几乎所有情况下,传递NULL的代码简化都会导致调用API两次并在堆上分配少量内存的性能影响可忽略不计。

这样您就不必担心更复杂的错误处理和分配/释放逻辑。你也避免了潜在的安全问题。

说,在提供的示例中使用MAX_PATH并调用RegQueryValueEx一次可能是最好的选择。