在全局单例对象中存储 COM 指针时出现问题
-
19-09-2019 - |
题
背景
我正在使用的应用程序有几个 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_pMyOtherObject
是一个成员变量 CComPtr
类型。
延迟初始化可能与我的问题无关,但为了完整性我将其包括在内。
问题
我注意到,在某些情况下,当我的应用程序关闭时,我会得到失败的断言。但是,如果我更改代码来调用 QueryInterface()
每一个 我需要访问的时间 IID_IMyOtherOBject
(而不是将其存储为成员变量)这会阻止断言。
在我看来,这是一个 COM 对象生命周期问题。我的假设是因为我是 storing
一个 COM 指针,在我指向的 COM 接口的销毁和我自己的指向它的指针之间需要有某种同步。
我的理解 CComPtr
类(我正在使用的)的优点是它消除了处理生命周期问题的很多麻烦(即。呼叫 AddRef()
和 Release()
)。但它似乎不适用于我的情况。
谁能挑出我可能做错了什么?
其他提示
您会返回智能指针可能不会增加引用计数的参考。对不起,我会检查,但已经晚了这里。这是我的直觉,它适合你描述 - 看看拷贝构造函数但是CComPtr
希望帮助,
ķ
在黑暗中刺野:有没有可能是CGlobalSingleton
可以被摧毁的之后的CoUninitialize()
被调用时,在任何情况下?如果是这样,和m_pMyOtherObject
是因此也COM反初始化后销毁,它会导致访问冲突伊戈尔提到的另一种方式。
我怀疑问题出在你的但是CComPtr类的复制/赋值语义的理解;我不是特别熟悉,但是CComPtr,但以我的经验智能指针往往不工作,你可能期望他们的方式。首先,你应该阅读的但是CComPtr文件,确保你了解它是如何工作(它不会伤害看看源代码,要么)。你也可以尝试把一些断点中的AddRef()和Release()但是CComPtr的成员查看期间调用GetMyOtherObject(之后会发生什么),特别是如果你是临时存储的返回值,它超出范围。
听上去像 m_pMyOtherObject
当您关闭应用程序时仍然存在。除了复制构造函数问题 m_pMyOtherObject
应该是 CComPtr
或者 CGlobalSingleton
应该打电话 m_pMyOtherObject
的 Release
销毁时的方法。
为了清楚起见进行了编辑。
编辑 刚刚做了一个快速测试,使用返回引用的函数没有遇到任何问题 CComPtr
. 。虽然这有点不寻常,但它并没有导致任何引用计数问题。
我想扩展一下如果 m_pMyOtherObject
不是智能指针。在这种情况下,它永远不会被释放。让我告诉你原因:
- 您在某个指针上调用 QueryInterface。它将对该对象调用 AddRef。
- 您返回 CComPtr& CComPtr& 或裸接口指针。这在很大程度上是无关紧要的。不会发生引用计数操作(除非您将返回值分配给另一个 CComPtr,这将 AddRef 它。但由于 CComPtr 将通过调用 Release 来平衡它,所以这并不重要)。
- 最终的结果是要么 1 次调用 AddRef,0 次调用 Release,要么 2 次调用 AddRef,1 次调用 Release。换句话说,它们不平衡,并且存在参考泄漏。
为了避免这种情况,你需要像这样构建你的程序:
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;
}
}