1

我正在维护一个使用 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 向我显示,每次运行时我似乎都在泄漏pFolder和实例。pShellWindow我无法弄清楚为什么会发生这种情况,因为据我所知,这些都已正确发布。谁能看到我错过了什么?

4

2 回答 2

5

pShellWindows在 之后释放CoUninitialize,这是不正确的。

其余接口似乎发布得很好。请注意,您可以通过使用而不是,并且根本不使用原始指针(,)并用智能自动释放包装器替换它们来大大提高清洁度和可读性。CComQIPtrQueryInterfaceBSTRIFoo*

pFolder如果Item调用成功但返回的代码不是S_OK. 同样,使用 ofCComPtr<IFolder>而不是IFolder*会立即解决此问题,甚至不会引起任何注意。

于 2013-05-03T09:25:15.043 回答
4
CoInitialize(NULL);

这一说法的问题不止一个。@Roman 解释了如何通过过早取消初始化来泄漏。但这也会在不止一个方面变得糟糕,线程的单元状态在 COM 中是一件非常重要的事情:

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

  • 选择 STA 还要求您实现单线程单元的协定。这表明你永远不会阻塞线程,你可以接受。 并且你泵出一个消息循环。消息循环对于封送对单线程单元的调用至关重要。你对此不以为然,你也不能合理地确保在这样的函数中解决这个问题。对于 shell 接口尤其重要,它们中的绝大多数都不是线程安全的。不抽水的后果就是死锁。你可以不抽水而侥幸逃脱,这不是一个保证的死锁。由于这些可能是进程外接口,因此您将在这里获得一些回旋余地。

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

于 2013-05-03T11:02:22.253 回答