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();
}

私のプログラムがこの更新を定期的に行うと、サイズがゆっくりと大きくなり、実行するたびにリークpFolderとインスタンスが発生しているように見えることが UMDH によって示されることに気付きました。pShellWindow私が知る限り、これらは適切にリリースされているため、一体なぜこれが起こるのかわかりません。誰かが私が見逃しているものを見ることができますか?

4

2 に答える 2

5

pShellWindowsの後にリリースしますCoUninitializeが、これは正しくありません。

残りのインターフェイスは問題なくリリースされているようです。の代わりに使用し、生のポインター ( 、 )をまったく使用しないことで、クリーンレスと読みやすさを大幅に改善し、それらをスマートな自動解放ラッパーに置き換えることができることに注意してください。CComQIPtrQueryInterfaceBSTRIFoo*

pFolderItem呼び出しは成功したが、以外のコードを返す場合は、リークしている可能性もありS_OKます。繰り返しになりますが、CComPtr<IFolder>代わりに ofIFolder*を使用すると、この問題に注意を向けることなく、すぐにこの問題を解決できます。

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

このステートメントには複数の問題があります。@Roman は、あまりにも早く初期化を解除することでどのようにリークするかを説明しました。しかし、これは複数の方法でうまくいかないこともあります。スレッドのアパートメント状態は、COM では本当に大きな問題です。

  • CoInitialize() の戻り値をチェックしていません。これにより、CoInitializeEx() が既に呼び出され、STA ではなく MTA が選択されている場合、この関数を呼び出すクライアント アプリが爆発します。これにより、CoInitialize() が失敗し、コミット後にスレッドの状態を変更できなくなります。CoUninitialize() 呼び出しは、クライアント アプリを粉々に吹き飛ばし、その後のすべての COM 呼び出しを失敗させます。

  • STA を選択するには、シングル スレッド アパートメントのコントラクトを実装する必要もあります。スレッドを決してブロックしないと述べていますが、それで問題ありません。 そして、メッセージループをポンピングします。メッセージ ループは、シングル スレッド アパートメントへの呼び出しをマーシャリングするために重要です。あなたはそれで大丈夫ではありませんし、このような関数でこれが処理されることを合理的に保証することもできません. シェル インターフェイスにとって特に重要なのは、それらの大部分がスレッド セーフではないことです。ポンピングしないことの結果は、デッドロックです。ポンピングしないとうまくいくかもしれませんが、デッドロックが保証されているわけではありません。これらはおそらくプロセス外のインターフェイスであるため、ここでは少し余裕があります。

特に最後の要件は、この関数を呼び出すスレッドを作成したコードによってのみ満たすことができます。関数を呼び出す以外にスレッドが行うことを制御できるのはコードだけです。クライアント アプリが COM を正しく初期化するという保証が得られない場合、真に安全な唯一の方法は、自分でスレッドを作成することです。

于 2013-05-03T11:02:22.253 に答える