我正在使用的应用程序有几个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_pMyOtherObject
是CComPtr
类型的成员变量。
延迟初始化可能与我的问题无关,但我将其包括在内是为了完整性。
我注意到的是,在某些情况下,当我的应用程序关闭时,我的断言失败了。但是,如果我每隔时间更改我的代码以调用QueryInterface()
,我需要访问IID_IMyOtherOBject
(而不是将其存储为成员变量),这会阻止断言。
在我看来这是一个COM对象生命周期问题。我的假设是,因为我是storing
一个COM指针,所以我指向的COM接口的销毁和我自己指向它的指针之间需要某种同步。
我对CComPtr
类(我正在使用的)的理解是它消除了处理生命周期问题的许多麻烦(即调用AddRef()
和Release()
)。但它似乎并不适用于我的情况。
任何人都可以选择我可能做错的事吗?
答案 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
应该是CComPtr
或CGlobalSingleton
应该在销毁时调用m_pMyOtherObject
的{{1}}方法。
为了清晰而编辑。
编辑刚刚进行了快速测试,并且在使用返回Release
引用的函数时没有遇到任何问题。虽然这有点不寻常,但它没有引起任何引用计数问题。
我希望扩展一下如果CComPtr
不是智能指针会发生什么。在这种情况下,它永远不会被释放。让我告诉你原因:
为避免这种情况,您需要按照以下方式构建程序:
m_pMyOtherObject