为什么这个COM代码泄漏了?

时间:2013-05-03 09:19:21

标签: c++ windows com atl windows-shell

我正在维护一个使用Windows资源管理器覆盖图标的应用程序。偶尔有些操作要求我强行刷新特定文件夹的资源管理器视图。我使用以下使用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个实例。我无法解决为什么在地球上发生这种情况,因为据我所知,这些都是正确释放的。谁能看到我错过的东西?

2 个答案:

答案 0 :(得分:4)

您在pShellWindows之后发布CoUninitialize,这是不正确的。

其余界面似乎很好。请注意,您可以使用CComQIPtr代替QueryInterface,而不是使用原始指针(BSTRIFoo*来提高 的无尘和可读性并用智能自动释放包装纸替换它们。

如果pFolder调用成功但返回Item以外的代码,则

S_OK也可能泄漏。同样,使用CComPtr<IFolder>代替IFolder*可以立即解决此问题,甚至无需关注它。

答案 1 :(得分:4)

CoInitialize(NULL);

此声明存在多个问题。 @Roman解释了如何通过过早的初始化来泄漏。但这也会在不止一种方式上变坏,线程的公寓状态是COM中真正的大交易:

  • 您没有检查CoInitialize()的返回值。如果它已经调用了CoInitializeEx()并选择了MTA而不是STA,那么这将会调用调用此函数的客户端应用程序。这将使CoInitialize()失败,您无法在提交后更改线程状态。您的CoUninitialize()调用会将客户端应用程序吹向smithereens,使其后续的所有COM调用失败。

  • 选择STA还要求您为单线程公寓实施合同。哪个说明你永远不会阻止这个帖子,你就可以了。 你抽一个消息循环。消息循环对于编组对单线程单元的调用至关重要。你没关系,你也不能合理地确保在这样的函数中处理这个问题。对于shell接口尤其重要,绝大多数都不是线程安全的。不抽水的后果是僵局。你可能躲避不抽水,这不是一个保证死锁。你可以在这里获得一些余地,因为这些可能是进程外接口。

特别是最后一个要求只能通过创建调用此函数的线程的代码来满足,只有它可以控制线程在调用函数之外的作用。如果您无法保证客户端应用程序正确初始化COM,那么唯一真正安全的做法就是自己创建一个线程。