由于我目前需要在C#应用程序中使用Windows API IUnknown_SetSite()
,因此我试图了解此功能的内部功能。我找到了this reference,后者又将我转发给this page,其中说明了以下内容:
对象应该保留在this指针上,调用IUnknown :: AddRef 这样做。如果对象已经有一个站点,它应该调用它 现有网站的IUnknown :: Release,保存新网站指针,然后调用 新网站的IUnknown :: AddRef。
现在,请考虑以下代码(假设我已在其他地方正确声明了Windows API' s原型,接口,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
。因此,我不能在以后发布该界面。
这没有意义恕我直言,根据我所做的一些测试,这不是真的(如果我的测试方法是正确的,ComClass_1
对象' IUnknown
参考计数器在执行最后一行期间不减少)。
但是,因为正确释放或不释放COM接口是至关重要的,所以我想知道那里发生了什么。问题可以归结为文档在表示" ...有网站时的含义......"。
就我个人而言,我认为它必须是#34; ...还有另一个网站,而不是对象本身......",但我非常想知道那里的COM /互操作专家是怎么想的关于它。
答案 0 :(得分:1)
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();
...如果非null,则在销毁对象时释放this->m_pSite
。 this->m_pSite
以NULL开头,因为该对象未连接到站点。如果对象是线程安全的,则在将新指针分配给InterlockedExchangePointer
时将使用this->m_pSite
。 IUnknown_Set
可以为SetSite的简单实现完成所有这些工作。 MSDN确实说实现应该首先发布然后发布AddRef,但是如果每个人都遵循COM规则,则顺序无关紧要。调用者已经拥有对他们传入的站点的引用,因此即使旧站点和新站点是同一个对象实例,它也不会被版本销毁。
答案其实很简单。您不必担心任何事情,SetSite实现将AddRef新站点并在不再需要站点时释放它。传递NULL和任何接口指针作为新站点除了对象本身(foo->SetSite(foo);
)是安全的,因为这样对象永远不会被释放。多次使用相同指针调用SetSite也是安全的。