gpt4 book ai didi

c# - IUnknown_SetSite 引用计数器行为

转载 作者:行者123 更新时间:2023-11-28 05:17:59 25 4
gpt4 key购买 nike

因为我目前需要在 C# 应用程序中使用 Windows API 的 IUnknown_SetSite(),所以我试图了解此函数的内部结构。我找到了 this reference这又将我转发给this page声明如下:

The object should hold onto this pointer, calling IUnknown::AddRef in doing so. If the object already has a site, it should call that existing site's IUnknown::Release, save the new site pointer, and call the new site's IUnknown::AddRef.

现在,请考虑以下代码(假设我已经在其他地方正确声明了 Windows API 的原型(prototype)、接口(interface)、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 对象本身。因此,如果我从字面上理解引用,该对象将在执行最后一行期间在其自己的 IUnknown 接口(interface)上调用 Release()。因此,我不能稍后发布该接口(interface)。

恕我直言,这没有意义,根据我所做的一些测试,这是不正确的(如果我的测试方法是正确的,ComClass_1 对象的 IUnknown 引用计数器确实在最后一行的执行期间减少)。

但是由于正确释放或不释放 COM 接口(interface)至关重要,所以我想确定那里发生了什么。问题归结为文档在说“......有一个网站......”时的意思。

就我个人而言,我认为它一定是“......有另一个站点而不是对象本身......”,但我非常想知道那里的 COM/interop 专家是怎么想的。

最佳答案

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 生命周期管理,它会尝试几个接口(interface),而不仅仅是 IObjectWithSite。

如果对象实现了具有 SetSite 方法的接口(interface)之一,它的实现应该看起来像这样:

IUnknown *pOld = this->m_pSite;
if (pUnkSite) pUnkSite->AddRef();
this->m_pSite = pUnkSite;
if (pOld) pOld->Release();

...如果非空,this->m_pSite 在对象被销毁时被释放。 this->m_pSite 开始时为 NULL,因为该对象未连接到站点。如果对象是线程安全的,它将在将新指针分配给 this->m_pSite 时使用 InterlockedExchangePointerIUnknown_Set 可以为 SetSite 的简单实现做所有这些。 MSDN 确实说实现应该先 Release 然后 AddRef 但如果每个人都遵循 COM 规则,顺序并不重要。调用者已经拥有对他们传入的站点的引用,因此即使旧站点和新站点是同一对象实例,它也不会被 Release 销毁。

其实答案很简单。您不必担心任何事情,SetSite 实现将 AddRef 新站点并在不再需要站点时释放它。将 NULL 和任何接口(interface)指针作为新站点传递是安全的 对象本身 (foo->SetSite(foo);),因为这样对象将永远不会被释放.使用同一个指针多次调用 SetSite 也是安全的。

关于c# - IUnknown_SetSite 引用计数器行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42205981/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com