2013-05-03 180 views
0

我正在維護使用Windows資源管理器覆蓋圖標的應用程序。偶爾有些操作要求我強制刷新某個特定文件夾的瀏覽器視圖。我這樣做使用下面的函數,它使用COM:爲什麼這個COM代碼泄漏?

void RefreshExplorerView(CString strPath) 
{ 
    CComPtr<IShellWindows> pShellWindows; 

    CoInitialize(NULL); 

    if(SUCCEEDED(pShellWindows.CoCreateInstance(CLSID_ShellWindows))) 
    { 
     IDispatch* pFolder=NULL; 
     VARIANT variant; 
     V_VT(&variant) = VT_I4; 

     for(V_I4(&variant) = 0; pShellWindows->Item(variant, &pFolder) == S_OK; V_I4(&variant)++) 
     { 
      CComPtr<IWebBrowserApp> pWebBrowserApp; 
      if(SUCCEEDED(pFolder->QueryInterface(IID_PPV_ARGS(&pWebBrowserApp)))) 
      { 
       BSTR LocationURL = NULL; 
       pWebBrowserApp->get_LocationURL(&LocationURL); 

       if(LocationURL != NULL && strPath.CompareNoCase(LocationURL) == 0) 
       { 
        CComPtr<IServiceProvider> pServiceProvider; 
        if(SUCCEEDED(pWebBrowserApp->QueryInterface(IID_PPV_ARGS(&pServiceProvider)))) 
        { 
         CComPtr<IShellBrowser> pShellBrowser; 
         if(SUCCEEDED(pServiceProvider->QueryInterface(IID_PPV_ARGS(&pShellBrowser)))) 
         { 
          IShellView* pShellView; 
          if(SUCCEEDED(pShellBrowser->QueryActiveShellView(&pShellView))) 
          { 
           pShellView->Refresh(); 
           pShellView->Release(); 
          } 
         } 
        } 
       } 

       SysFreeString(LocationURL); 
      } 
      pFolder->Release(); 
      pFolder = NULL; 
     } 
    } 

    CoUninitialize(); 
} 

我發現我的程序做這個刷新的時候經常是慢慢生長在大小和UMDH指示我,我似乎被泄漏pFolderpShellWindow實例每次運行。我無法弄清楚爲什麼發生這種情況,因爲據我所知可以正確釋放。任何人都可以看到我失蹤的東西嗎?

+1

您可以用_variant_t(comutil.h)替換CComPtrs和BSTR的_bstr_t和VARIANT的IDispatch&IShellView,這樣就可以確保沒有泄漏。 – 2013-05-03 09:55:00

回答

4

您釋放pShellWindowsCoUninitialize,這是不正確的。

接口的其餘部分似乎被釋放罰款。請注意,通過使用CComQIPtr而不是QueryInterface,並且完全不使用原始指針(BSTR,IFoo*),並用智能自動釋放包裝代替它們,您可以提高清潔度和可讀性大大提高

pFolder也可能泄漏,如果Item調用成功但返回的代碼不是S_OK。再次,使用CComPtr<IFolder>而不是IFolder*將立即解決此問題,甚至沒有引起任何關注。

+0

謝謝,不能相信我沒有發現與pShellWindows問題;-) – Benj 2013-05-03 09:53:02

4
CoInitialize(NULL); 

此聲明存在多個問題。 @羅曼解釋瞭如何通過不太快的初始化來泄漏。但是,這也將走壞不止一種方法,一個線程的單元狀態是真正的大事在COM:

  • 你是不是檢查的CoInitialize的返回值()。如果它已經調用了CoInitializeEx()並且選擇了MTA而不是STA,這將炸燬調用此函數的客戶端應用程序。這會使CoInitialize()失敗,您不能在提交之後更改線程狀態。您的CoUninitialize()調用將使客戶端應用程序陷入混亂,導致其後續的COM調用失敗。

  • 選擇STA還要求您執行單線程單元的合同。其中說,你永遠不會阻止線程,你沒關係。 您泵送消息循環。消息循環對封送到單線程公寓的呼叫至關重要。你是而不是沒關係,你也不能合理地確保這是在這樣的功能照顧。對於shell接口來說尤其重要,絕大多數不是線程安全的。不抽水的後果是僵局。你可能擺脫不抽水,它不是一個保證的僵局。由於這些可能是進程外接口,你會在這裏獲得一些餘地。

特別是最後一條要求只能由創建調用該函數的線程的代碼來滿足,只有它是在線程做超越呼喚你的功能是什麼控制。如果你不能保證客戶端應用程序正確初始化COM,那麼唯一真正安全的事情就是自己創建一個線程。

+0

在這種情況下,該函數總是由一個新創建的線程調用,該線程已經被創建來處理管道消息,它應該緩解你的第一點(儘管我仍然會檢查返回值)。你的第二點確實給了我一些關注,但是,我可以通過簡單地使用多線程模型來緩解這一點嗎? – Benj 2013-05-03 12:20:29

+0

MTA中沒有免費的午餐,您可以將其留給其他代碼來照顧以線程安全的方式調用線程不安全的代碼。最好把角鬥掉。我只是告訴你什麼可能出錯,當你看到代碼死鎖時,你會知道什麼是錯的。 – 2013-05-03 13:36:43