我需要为VB6应用程序提供一个函数,它枚举当前用户的打印机。由于列出网络上的所有打印机,内置的VB6打印机对象在终端服务器上失败。通过IShellFolder接口枚举打印机文件夹导致堆分配泄漏
在Win7 x64 SP1上使用VC2013 Update 5编译。检查省略错误
#include <Windows.h>
#include <ShlObj.h>
#pragma comment(lib, "Shell32.lib")
int main(int argc, wchar_t* argv[])
{
HRESULT hr = CoInitialize(0);
ULONG ulFetched = 0;
LPITEMIDLIST pPidl = NULL;
IShellFolder *pPrinterFolder = NULL;
IEnumIDList *pEnum = NULL;
IShellFolder *pDesktopFolder = NULL;
hr = SHGetDesktopFolder(&pDesktopFolder);
LPITEMIDLIST pPidlLocation = NULL;
hr = SHGetSpecialFolderLocation(NULL, CSIDL_PRINTERS, &pPidlLocation);
hr = pDesktopFolder->BindToObject(pPidlLocation, NULL, IID_IShellFolder, (void **)&pPrinterFolder);
hr = pPrinterFolder->EnumObjects(NULL, SHCONTF_NONFOLDERS, &pEnum);
while ((hr = pEnum->Next(1, &pPidl, &ulFetched)) == S_OK && ulFetched > 0)
{
// Do something with item
CoTaskMemFree(pPidl);
}
CoTaskMemFree(pPidlLocation);
pEnum->Release();
pPrinterFolder->Release();
pDesktopFolder->Release();
// Heap allocation leak
CoUninitialize();
return 0;
}
的问题是,调用CoUnitialize()
导致堆分配泄漏与应用验证被监控时:注意
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<avrf:logfile xmlns:avrf="Application Verifier">
<avrf:logSession TimeStarted="2015-10-06 : 13:13:37" PID="1880" Version="2">
<avrf:logEntry Time="2015-10-06 : 13:13:40" LayerName="Leak" StopCode="0x900" Severity="Error">
<avrf:message>A heap allocation was leaked.</avrf:message>
<avrf:parameter1>6ff7ff0 - Address of the leaked allocation. Run !heap -p -a <address> to get additional information about the allocation.</avrf:parameter1>
<avrf:parameter2>49a5774 - Address to the allocation stack trace. Run dps <address> to view the allocation stack.</avrf:parameter2>
<avrf:parameter3>5bd8fe8 - Address of the owner dll name. Run du <address> to read the dll name.</avrf:parameter3>
<avrf:parameter4>11390000 - Base of the owner dll. Run .reload <dll_name> = <address> to reload the owner dll. Use 'lm' to get more information about the loaded and unloaded modules.</avrf:parameter4>
<avrf:stackTrace>
<avrf:trace>vfbasics!+59d8a7b7 (@ 0)</avrf:trace>
<avrf:trace>vfbasics!+59d8b031 (@ 0)</avrf:trace>
<avrf:trace>vfbasics!+59d86ac4 (@ 0)</avrf:trace>
<avrf:trace>ntdll!RtlApplicationVerifierStop+1a6 (@ 0)</avrf:trace>
<avrf:trace>ntdll!RtlUlonglongByteSwap+222e (@ 0)</avrf:trace>
<avrf:trace>ntdll!LdrUnloadDll+4a (@ 0)</avrf:trace>
<avrf:trace>vfbasics!+59d87065 (@ 0)</avrf:trace>
<avrf:trace>KERNELBASE!FreeLibrary+15 (@ 0)</avrf:trace>
<avrf:trace>ole32!PropVariantCopy+746 (@ 0)</avrf:trace>
<avrf:trace>ole32!PropVariantCopy+81c (@ 0)</avrf:trace>
<avrf:trace>ole32!PropVariantCopy+830 (@ 0)</avrf:trace>
<avrf:trace>ole32!PropVariantCopy+7b7 (@ 0)</avrf:trace>
<avrf:trace>ole32!SetErrorInfo+75 (@ 0)</avrf:trace>
<avrf:trace>vfbasics!+59d8ee93 (@ 0)</avrf:trace>
<avrf:trace>userprinters!main+183 (c:\projects\userprinters\userprinters\userprinters.cpp @ 44)</avrf:trace>
<avrf:trace>userprinters!__tmainCRTStartup+199 (f:\dd\vctools\crt\crtw32\dllstuff\crtexe.c @ 626)</avrf:trace>
<avrf:trace>userprinters!mainCRTStartup+d (f:\dd\vctools\crt\crtw32\dllstuff\crtexe.c @ 466)</avrf:trace>
<avrf:trace>kernel32!BaseThreadInitThunk+12 (@ 0)</avrf:trace>
<avrf:trace>ntdll!RtlInitializeExceptionChain+63 (@ 0)</avrf:trace>
<avrf:trace>ntdll!RtlInitializeExceptionChain+36 (@ 0)</avrf:trace>
</avrf:stackTrace>
</avrf:logEntry>
</avrf:logSession>
</avrf:logfile>
作为一个侧面说明,似乎CoInitialize()
似乎是最近的Windows版本的要求,如所述here
任何人都可以指出我正确的方向是什么导致这种泄漏?
最有可能从应用程序验证假阳性 - https://social.msdn.microsoft.com/Forums/en-US/9526dcff-03fc-4aa3- 8fea-7c5e0512bd9d/leak-found-by-appverifier-if-coinitializeex-in-constructor?forum = vssmartdevicesnative – wqw
顺便说一句,你也可以在VB6中实现这个枚举 - http://stackoverflow.com/questions/1063874/retrieving-icons当前用户打印机 – wqw
只要所有COM调用都成功(因为您没有执行任何错误处理),所显示的代码中没有泄漏。顺便说一句,如果IEnumIDList :: Next()返回S_OK,它永远不会将ulFetched设置为0。由于您一次请求1个PIDL,因此只能返回<0为错误,S_OK(ulFetched = 1)成功,S_FALSE(ulFetched = 0)返回枚举结束。在这个例子中你不需要'ulFetched',当'celt = 1'调用'Next()'时,你可以设置'pceltFetched = NULL'。文件甚至这样说。 –