返回可变大小数据的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);
我的问题:是否有可靠的处理此类功能的模式/模板?例如,如果内存实际耗尽,或者两次调用之间的大小是否发生变化?
答案 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一次可能是最好的选择。