2013-05-03 134 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