0

背景

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

其中一个 COM DLL 有一个全局单例对象,它存储指向其他 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_pMyOtherObjectCComPtr类型的成员变量。

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

问题

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

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

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

谁能选择我可能做错了什么?

4

5 回答 5

2

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

于 2009-12-11T02:14:18.137 回答
2

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

于 2009-12-11T09:46:08.400 回答
2

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

希望有帮助,

ķ

于 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或者应该在销毁时CGlobalSingleton调用m_pMyOtherObject'方法。Release

为清晰起见进行了编辑。

编辑刚刚做了一个快速测试,使用返回引用的函数没有遇到任何问题CComPtr。虽然这有点不寻常,但它不会导致任何引用计数问题。

我想扩展如果m_pMyOtherObject不是智能指针会发生什么。在这种情况下,它永远不会被释放。让我告诉你为什么:

  1. 您在某个指针上调用 QueryInterface。它将在该对象上调用 AddRef。
  2. 您返回 CComPtr& CComPtr& 或裸接口指针。这在很大程度上是无关紧要的。不会发生引用计数操作(除非您将返回值分配给另一个 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 回答