由于我目前需要在一个IUnknown_SetSite()应用程序中使用Windows的IUnknown_SetSite(),所以我试图了解这个函数的内部结构。我找到了本参考,它将我转发给此页,该此页声明如下:
该对象应该保持此指针,在此过程中调用IUni知名::AddRef。如果对象已经有一个站点,那么它应该调用现有站点的IUnioking::Release,保存新的站点指针,并调用新站点的IUnioking::AddRef。
现在,请考虑以下代码(假设我在其他地方正确声明了Windows的原型、接口、GUID等以及我正在使用的变量):
/* Create COM Object of ComClass_1 and get reference to its IUnknown */
CoCreateInstance(ref ComClass_1_id,
IntPtr.Zero,
(CLSCTX.CLSCTX_INPROC_SERVER | CLSCTX.CLSCTX_LOCAL_SERVER),
ref IID_IUNKNOWN,
out intptr_ComClass_1_IUnknown);
/* This actually is not necessary to understand the question itself, but
keeps additional complexity from us (without the following line, the
ComClass_1 object would destroy itself if the reference counter for its
IUnknown interface would reach zero, and for this question, I would like
to keep this aspect from being discussed) */
CoCreateInstance(ref ComClass_1_id,
IntPtr.Zero,
(CLSCTX.CLSCTX_INPROC_SERVER | CLSCTX.CLSCTX_LOCAL_SERVER),
ref IID_I_SOME_OTHER_INTERFACE,
out intptr_ComClass_1_ISomeOtherInterface);
/* Create COM Object of ComClass_2 and get reference to its IUnknown */
CoCreateInstance(ref ComClass_2_id,
IntPtr.Zero,
(CLSCTX.CLSCTX_INPROC_SERVER | CLSCTX.CLSCTX_LOCAL_SERVER),
ref IID_IUNKNOWN,
out intptr_ComClass_2_IUnknown);
/* Now set ComClass_1 object's site to ComClass_2 object */
IUnknown_SetSite(intptr_ComClass_1_IUnknown, intptr_ComClass_2_IUnknown);我的问题其实很简单:
在第一行之后,ComClass_1对象的站点显然是ComClass_1对象本身。因此,如果我从字面上理解引用,那么在执行最后一行时,该对象将在其自己的Release()接口上调用IUnknown。因此,我不能在以后发布这个接口。
这是没有意义的IMHO,而且根据我所做的一些测试,这是不正确的(如果我的测试方法是正确的,ComClass_1对象的IUnknown引用计数器在执行最后一行时没有减少)。
但是,由于正确地发布或不释放COM接口至关重要,我想确切地知道那里发生了什么。这个问题归结为当文档中写着“.有一个站点.”时,文档意味着什么。
就我个人而言,我认为它一定是“.有另一个站点,而不是对象本身.”,但我非常想知道外面的COM / interop专家对此有何看法。
发布于 2017-02-15 03:08:06
TL;DR:你的假设是不正确的,对象不是以自身作为站点创建的。只需调用SetSite并让对象处理与可能拥有的现有旧站点相关的生存期问题。您只负责您创建的/AddRef/QueryInterface对象。
对象通常从不使用自己作为站点。对象站点通常以NULL开头。站点用于将两个对象连接在一起,连接是一种方式,并允许对象与其主机/所有者交互。您通常是在实现站点或,即自己需要站点的对象。
IUnknown_SetSite只是一个辅助函数,它所做的就是:
hr = ptr->QueryInterface(IObjectWithSite, &i1);
if (SUCCEEDED(hr))
{
hr = i1->SetSite(unkSite);
i1->Release();
if (SUCCEEDED(hr)) return hr;
}
hr = ptr->QueryInterface(ISomeOtherInterfaceThatHasASetSiteMethod, &i2);
if (SUCCEEDED(hr)) ...
...它所做的一切都是正常的COM生命周期管理,它尝试了几个接口,而不仅仅是IObjectWithSite。
如果对象实现了一个具有SetSite方法的接口,那么它的实现应该如下所示:
IUnknown *pOld = this->m_pSite;
if (pUnkSite) pUnkSite->AddRef();
this->m_pSite = pUnkSite;
if (pOld) pOld->Release();...and如果非空,则在销毁对象时释放this->m_pSite .this->m_pSite从NULL开始,因为对象没有连接到站点。如果对象是线程安全的,则在将新指针分配给InterlockedExchangePointer时将使用this->m_pSite。IUnknown_Set可以为SetSite的简单实现完成所有这一切。MSDN确实说,实现应该先发布,然后发布AddRef,但是如果每个人都遵循COM规则,那么顺序并不重要。调用方已经有了对他们传入的站点的引用,因此即使旧站点和新站点是相同的对象实例,它也不能被发布销毁。
答案其实很简单。您不必担心任何事情,SetSite实现将AddRef新站点并在它不再需要站点时发布它。将NULL和任何接口指针作为新站点传递是安全的,但对象本身(foo->SetSite(foo);)除外,因为这样就永远不会释放对象。使用同一个指针多次调用SetSite也是安全的。
https://stackoverflow.com/questions/42205981
复制相似问题