c++ - 通过 IShellFolder 接口(interface)枚举打印机文件夹导致堆分配泄漏

标签 c++ winapi memory-leaks com vb6

我需要为 VB6 应用程序提供一个函数,它枚举当前用户的打印机。由于列出网络上的所有打印机,内置 VB6 打印机对象在终端服务器上失败。

在 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 &lt;address&gt; to get additional information about the allocation.</avrf:parameter1>
            <avrf:parameter2>49a5774 - Address to the allocation stack trace. Run dps &lt;address&gt; to view the allocation stack.</avrf:parameter2>
            <avrf:parameter3>5bd8fe8 - Address of the owner dll name. Run du &lt;address&gt; to read the dll name.</avrf:parameter3>
            <avrf:parameter4>11390000 - Base of the owner dll. Run .reload &lt;dll_name&gt; = &lt;address&gt; to reload the owner dll. Use &apos;lm&apos; 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 所述

任何人都可以指出导致此泄漏的正确方向吗?

最佳答案

是的,处理打印机枚举的模块 prncache.dll 和 prhfldr.dll 似乎存在泄漏。

首先,我对您的代码进行了一些修改,添加了一个循环来检查任务管理器中的内存使用情况:

while (true)
{
    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();
}

而且我看到内存使用量在增长。

然后我使用 Deleaker,在 CoInitialize() 的行设置断点并制作了几个快照以供稍后比较。

这是我得到的:

leaks

关于c++ - 通过 IShellFolder 接口(interface)枚举打印机文件夹导致堆分配泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32969306/

相关文章:

c++ - 是否有用于使用 gcc 构建 MS Visual C++ Express 的免费开源工具?

c++ - 如何为托管 C++ 类中的静态变量赋值?

Android 内存泄漏和垃圾收集

ios - 合并/合并两个图像时,iPad 应用程序因内存警告而崩溃

c++ - 捕获键盘输入和钩子(Hook)的后果

c# - C# 应用程序内存泄漏?如何停止?

c++ - 这个 Visual Studio 编译器错误 'divide or mod by zero' 是一个错误吗?

Adobe After Effects 的 C# 互操作性

c++ - 如何获取 LPCTSTR 的子字符串?

perl 进程之间的 perl Win32 信号处理