0

バックグラウンド

私が使用しているアプリケーションには、いくつかのCOMDLLがあります。

COM DLLの1つには、他のDLLのCOMインターフェイスへのポインタを格納するグローバルシングルトンオブジェクトがあります。これはグローバルシングルトンオブジェクトであるため、ポインタを取得しようとしているインターフェイスがまだロードされていないDLLに存在する可能性があるため、レイジー初期化イディオムを採用しました。

補足:グローバルオブジェクトはプロセス内で構築されるため、単一のDLLを登録する場合、これは特に重要regsvr32です。このプロセス中にDLLが別のDLLへのインターフェイスを取得しようとしないようにします。)

たとえば、私の怠惰な初期化メソッドは次のようになります。

CComPtr<IMyOtherObject>&
CGlobalSingleton::
GetMyOtherObject()
{
    // SNIP: Other code removed for clarity...

    if (! m_pMyOtherObject)
    {
        hr = pUnknown->QueryInterface(IID_IMyOtherObject,
            (void**) &m_pMyOtherObject);
    }

    return m_pMyOtherObject;
}

注: m_pMyOtherObjectは、このタイプのメンバー変数ですCComPtr

怠惰な初期化はここでの私の問題とは関係がないかもしれませんが、完全を期すために含めています。

問題

私が気付いたのは、状況によっては、アプリケーションがシャットダウンしたときにアサーションが失敗することです。ただし、 (メンバー変数として格納するのではなく)アクセスする必要があるQueryInterface() たびに呼び出すようにコードを変更すると、アサーションが防止されます。IID_IMyOtherOBject

これは、COMオブジェクトの存続期間の問題であるように思われます。私の仮説は、私はstoringCOMポインターであるため、私が指しているCOMインターフェイスの破棄とそれに対する私自身のポインターとの間に何らかの同期が必要であるというものです。

(私が使用している)クラスについての私の理解は、それが生涯の問題(すなわち、電話と)CComPtrに対処することの多くの頭痛を取り除くということです。しかし、私の場合は機能していないようです。AddRef()Release()

誰かが私が間違っているかもしれないことを選ぶことができますか?

4

5 に答える 5

2

暗闇の中での野生の刺し傷:どのような状況でも、呼び出された後CGlobalSingletonに破壊される可能性はありますか?もしそうなら、そしてそれ故にCOMの初期化解除後にも破壊されたなら、それはIgorが言及したアクセス違反を引き起こすもう一つの方法でしょう。 CoUninitialize()m_pMyOtherObject

于 2009-12-11T09:46:08.400 に答える
2

独自のグローバルシングルトンを実装するのではなく、代わりにIGlobalInterfaceTableインターフェイスを使用することを検討してください。これは、プロセスレベルでOSによって提供されるシングルトンです。どのDLLもCOMオブジェクトをテーブルに配置でき、他のDLLは必要に応じてそれらを取得できます。自分の側で実装する必要があるのは、DLLがテーブルのDWORDCookieを相互に交換する方法だけです。

于 2009-12-11T02:14:18.137 に答える
2

スマートポインタへの参照を返していますが、参照カウントが増えていない可能性があります。申し訳ありませんが、確認しますが、ここでは遅れています。それは私の予感であり、あなたが説明していることに適合します-CComPtrのコピーコンストラクターを調べてください。

お役に立てば幸いです。

K

于 2009-12-11T00:49:22.723 に答える
1

問題は、CComPtrクラスのコピー/代入セマンティクスの理解にあると思います。私はCComPtrに特に精通していませんが、私の経験では、スマートポインターは期待どおりに機能しない傾向があります。まず、CComPtrのドキュメントを読み、それがどのように機能するかを理解していることを確認する必要があります(ソースコードを見ても問題はありません)。また、CComPtrのAddRef()およびRelease()メンバーにいくつかのブレークポイントを設定して、GetMyOtherObject()の呼び出し中および呼び出し後に何が起こるかを確認することもできます。特に、戻り値を一時的に保存していて、スコープ外になっている場合はそうです。

于 2009-12-11T02:03:24.793 に答える
0

m_pMyOtherObjectアプリケーションをシャットダウンしても、まだ生きているように聞こえます。コピーコンストラクターの問題に加えて、破棄時にのメソッドを呼び出すm_pMyOtherObjectか、CComPtr呼び出す必要があります。CGlobalSingletonm_pMyOtherObjectRelease

わかりやすくするために編集。

編集簡単なテストを行っただけで、への参照を返す関数を使用しても問題は発生しませんでしたCComPtr。これは少し珍しいことですが、参照カウントの問題は発生しませんでした。

m_pMyOtherObjectスマートポインタでない場合はどうなるかについて詳しく説明したいと思います。このシナリオでは、リリースされることはありません。理由をお見せしましょう。

  1. あるポインタでQueryInterfaceを呼び出します。そのオブジェクトに対してAddRefを呼び出します。
  2. CComPtr&CComPtr&またはネイキッドインターフェイスポインタのいずれかを返します。それはほとんど無関係です。refカウント操作は行われません(戻り値を別のCComPtrに割り当ててAddRefを実行しない限り。ただし、そのCComPtrはReleaseの呼び出しとバランスを取るため、問題ではありません)。
  3. 最終的には、AddRefへの1回の呼び出しとReleaseへの0の呼び出し、またはAddRefへの2回の呼び出しとReleaseへの1回の呼び出しのいずれかになります。言い換えれば、それらは不均衡であり、リファレンスリークがあります。

これを回避するには、プログラムを次のように構成する必要があります。

class CGlobalSingleton{

CComPtr<IMyOtherObject> m_spMyOtherObject;

IMyOtherObject* GetMyOtherObject()
{
    // SNIP: Other code removed for clarity...

    if (! m_spMyOtherObject)
    {
        //pUnknown gets AddRef'ed, but that's OK, m_spMyOtherObject will call release when CGlobalSingleton goes out of scope
        hr = pUnknown->QueryInterface(IID_IMyOtherObject,
            (void**) &m_spMyOtherObject);
    }

    return m_pMyOtherObject;
}
}
于 2009-12-11T02:12:19.757 に答える