在全局单例对象中存储COM指针的问题

时间:2009-12-11 00:13:16

标签: c++ com global-variables lazy-evaluation object-lifetime

背景

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

其中一个COM DLL有一个全局单例对象,它存储指向其他DLL中COM接口的指针。因为它是一个全局单例对象,所以我使用了lazy initialization成语,因为我试图获取指针的接口可能存在于尚未加载的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_pMyOtherObjectCComPtr类型的成员变量。

延迟初始化可能与我的问题无关,但我将其包括在内是为了完整性。

问题

我注意到的是,在某些情况下,当我的应用程序关闭时,我的断言失败了。但是,如果我每隔时间更改我的代码以调用QueryInterface() ,我需要访问IID_IMyOtherOBject(而不是将其存储为成员变量),这会阻止断言。

在我看来这是一个COM对象生命周期问题。我的假设是,因为我是storing一个COM指针,所以我指向的COM接口的销毁和我自己指向它的指针之间需要某种同步。

我对CComPtr类(我正在使用的)的理解是它消除了处理生命周期问题的许多麻烦(即调用AddRef()Release())。但它似乎并不适用于我的情况。

任何人都可以选择我可能做错的事吗?

5 个答案:

答案 0 :(得分:2)

您将返回对智能指针的引用,这可能不会增加引用计数。对不起,我会检查,但现在已经很晚了。这是我的预感,它适合你所描述的 - 查看CComPtr的复制构造函数。

希望有所帮助,

ķ

答案 1 :(得分:2)

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

答案 2 :(得分:2)

黑暗中的野蛮刺:在任何情况下调用 CGlobalSingleton之后,CoUninitialize()是否可能被摧毁?如果是,并且m_pMyOtherObject因此在COM未初始化后也被销毁,那么这将是导致Igor提到的访问冲突的另一种方式。

答案 3 :(得分:1)

我怀疑问题在于您对CComPtr类的复制/赋值语义的理解;我对CComPtr并不是特别熟悉,但根据我的经验,智能指针往往不会像你期望的那样工作。首先,您应该阅读CComPtr的文档,并确保您了解它的工作原理(查看源代码也没有什么坏处)。您还可以尝试在CComPtr的AddRef()和Release()成员中放置一些断点,以查看在调用GetMyOtherObject()期间和之后发生的情况,特别是如果您临时存储返回值并且超出范围。 / p>

答案 4 :(得分:0)

关闭应用程序时,m_pMyOtherObject的声音仍然存在。除了复制构造函数问题m_pMyOtherObject应该是CComPtrCGlobalSingleton应该在销毁时调用m_pMyOtherObject的{​​{1}}方法。

为了清晰而编辑。

编辑刚刚进行了快速测试,并且在使用返回Release引用的函数时没有遇到任何问题。虽然这有点不寻常,但它没有引起任何引用计数问题。

我希望扩展一下如果CComPtr不是智能指针会发生什么。在这种情况下,它永远不会被释放。让我告诉你原因:

  1. 您在某个指针上调用QueryInterface。它将在该对象上调用AddRef。
  2. 你返回CComPtr&amp;但是CComPtr和放大器;或裸露的界面指针。这基本上是无关紧要的。没有引用计数操作(除非你将返回值分配给另一个CComPtr,这将是AddRef它。但是因为CComPtr将通过调用Release来平衡它并不重要。)
  3. 您最终得到的是1次调用AddRef,0次调用Release或2次调用AddRef,1次调用Release。换句话说,他们是不平衡的,你有一个参考泄密。
  4. 为避免这种情况,您需要按照以下方式构建程序:

    m_pMyOtherObject