2015-10-06 48 views
0

我需要爲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 &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

任何人都可以指出我正確的方向是什麼導致這種泄漏?

+1

最有可能從應用程序驗證假陽性 - https://social.msdn.microsoft.com/Forums/en-US/9526dcff-03fc-4aa3- 8fea-7c5e0512bd9d/leak-found-by-appverifier-if-coinitializeex-in-constructor?forum = vssmartdevicesnative – wqw

+0

順便說一句,你也可以在VB6中實現這個枚舉 - http://stackoverflow.com/questions/1063874/retrieving-icons當前用戶打印機 – wqw

+1

只要所有COM調用都成功(因爲您沒有執行任何錯誤處理),所顯示的代碼中沒有泄漏。順便說一句,如果IEnumIDList :: Next()返回S_OK,它永遠不會將ulFetched設置爲0。由於您一次請求1個PIDL,因此只能返回<0爲錯誤,S_OK(ulFetched = 1)成功,S_FALSE(ulFetched = 0)返回枚舉結束。在這個例子中你不需要'ulFetched',當'celt = 1'調用'Next()'時,你可以設置'pceltFetched = NULL'。文件甚至這樣說。 –

回答

2

是的,看起來有模塊prncache.dll和prhfldr.dll處理打印機枚舉泄漏。

首先,我已經修改了你的代碼位的加算一個週期,以檢查內存使用在任務管理器:

我看到內存使用量越來越大。

然後我帶着Deleaker,在CoInitialize()的行設置了斷點,並做了幾個快照以便稍後進行比較。

在這裏我得到了什麼:

leaks

+0

感謝您調查此問題。因此,似乎在其中一個DLL中確實存在泄漏。它可能不會導致任何大問題,但泄漏內存仍然不正確。 – Aurora

+0

定義泄漏。這些DLL可能在設計時從來沒有打算一旦被shell封裝就卸載,所以釋放內存的代碼是無關膨脹的。 – wqw

+0

@Aurora作爲一種解決方法我將創建一個單獨的應用程序,它將枚舉並將結果返回給主應用程序。 –