为什么这个COM代码会泄漏

Why is this COM code leaking?

本文关键字:泄漏 代码 COM 为什么      更新时间:2023-10-16

我正在维护一个使用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实例。我不知道为什么会发生这种情况,因为据我所知,这些都是正确释放的。有人知道我错过了什么吗?

CoUninitialize之后释放pShellWindows,这是错误的。

其余的接口似乎发布得很好。请注意,您可以通过使用CComQIPtr而不是QueryInterface,并且根本不使用原始指针(BSTR, IFoo*)并将它们替换为智能自动释放包装器,从而大大改善整洁性和可读性如果Item调用成功但返回S_OK以外的代码,

pFolder也可能泄漏。同样,使用CComPtr<IFolder>代替IFolder*可以立即解决这个问题,甚至不会引起任何注意。

CoInitialize(NULL);

这个语句有不止一个问题。@Roman解释了过早不初始化是如何造成泄漏的。但是这也会以不止一种方式变坏,线程的公寓状态在COM中是一个真正的大问题:

  • 您没有检查CoInitialize()的返回值。如果客户端应用程序已经调用了CoInitializeEx(),并且选择了MTA而不是STA,那么调用该函数的客户端应用程序就会崩溃。这将使CoInitialize()失败,你不能在提交后更改线程状态。你的CoUninitialize()调用将会把客户端应用搞得一团糟,导致后续的所有COM调用都失败。

  • 选择STA还要求您实现单线程公寓的合同。它声明你永远不会阻塞线程,你可以这样做。表示您泵入消息循环。消息循环对于封送对单线程单元的调用至关重要。对于,您不能,也不能合理地确保在这样的函数中考虑到这一点。对于shell接口来说尤其重要的是,它们中的绝大多数都不是线程安全的。不抽运的后果是死锁。您可以不抽吸,但这并不能保证死锁。这里您可以有一点余地,因为这些可能是进程外接口。

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