代码之家  ›  专栏  ›  技术社区  ›  LeopardSkinPillBoxHat

在全局单例对象中存储COM指针时出现问题

  •  0
  • LeopardSkinPillBoxHat  · 技术社区  · 15 年前

    背景

    我正在使用的应用程序有几个COM DLL。

    其中一个COM DLL具有全局singleton对象,该对象存储指向其他DLL中COM接口的指针。因为它是一个全局单例对象,所以我使用了 lazy initialization 习惯用法,因为我试图获取的接口可能存在于尚未加载的dll中。

    ( 边注: 这在注册单个dll时尤其重要,因为全局对象将在 regsvr32 进程,我不希望该dll在此过程中尝试获取另一个dll的接口。)

    例如,我的lazy初始化方法将执行如下操作:

    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对象生存期问题。我的假设是因为我 storing 作为一个COM指针,我指向的COM接口的销毁和我自己指向它的指针之间需要某种同步。

    我的理解 CCOPTR 类(我正在使用)是它消除了处理终生问题(即打电话)的许多头痛。 AddRef() Release() )但在我的情况下似乎不起作用。

    有人能挑我做错了什么吗?

    5 回复  |  直到 15 年前
        1
  •  2
  •   Remy Lebeau    15 年前

    与其实现自己的全局单例,不如使用 IGlobalInterfaceTable 而是接口。它是由操作系统在进程级别提供的单例。任何DLL都可以将其COM对象放入表中,其他DLL可以在需要时检索它们。您只需要实现一种方法,让DLL可以相互交换表的dword cookie。

        2
  •  2
  •   Kevin Shea    15 年前

    您将返回对智能指针的引用,这可能不会增加引用计数。对不起,我会查一下,但这里很晚。这是我的预感,它符合您所描述的——查看ccompter的复制构造函数。

    希望能有所帮助,

    K

        3
  •  2
  •   Phil Booth    15 年前

    在黑暗中疯狂刺伤:有没有可能 CGlobalSingleton 可能会被摧毁 之后 CoUninitialize() 在任何情况下都会被召唤?如果是, m_pMyOtherObject 因此,在COM未初始化之后也会被破坏,这将是导致igor提到的访问冲突的另一种方式。

        4
  •  1
  •   Luke    15 年前

    我怀疑问题出在你对ccomptr类的复制/分配语义的理解上;我对ccomptr不是特别熟悉,但根据我的经验,智能指针的工作方式往往与你期望的不一样。首先,您应该阅读ccompter的文档,并确保您了解它的工作原理(查看源代码也不会造成伤害)。您还可以尝试在ccomptr的addRef()和release()成员中放置一些断点,以查看在调用getMyoTherObject()期间和之后会发生什么,特别是当您临时存储返回值并且它超出范围时。

        5
  •  0
  •   Igor Zevaka    15 年前

    听起来像 m_pMyOtherObject 关闭应用程序时仍处于活动状态。除了复制构造函数问题 M&U其他对象 应该是一个 CComPtr CGlobalSingleton 应该打电话 M&U其他对象 Release 销毁方法。

    为清晰起见而编辑。

    编辑 只是做了一个快速测试,使用函数返回引用时没有遇到任何问题 CCOPTR . 虽然这有点不寻常,但并没有引起任何参考计数问题。

    但我想扩展一下如果 M&U其他对象 不是智能指针。在这种情况下,它永远不会被释放。我来告诉你为什么:

    1. 在某个指针上调用queryinterface。它将对该对象调用addref。
    2. 返回ccomptr&ccomptr&或裸接口指针。这在很大程度上是无关紧要的。没有发生引用计数操作(除非您将返回值分配给另一个CCompter,后者将添加引用)。但是,因为这个ccompter会用一个释放的调用来平衡它,这并不重要)。
    3. 您最终得到的结果是1个调用addref和0个释放,或2个调用addref和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;
    }
    }