2

我们有一个 COM 组件,它的实现和接口定义存在于托管代码中,但由本机组件驱动。托管组件SafeArray通过以下方法声明返回到本机代码。

interface IExample {
  <return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType=VarEnum.VT_UNKNOWN)>
  object[] DoSomeOperation()
}

生成的本机签名正确地将其作为SafeArray.

在代码审查期间,尽管我们提出了一些关于使用 SafeArrayGetElement 调用结果数组的问题。问题是 SafeArrayGetElement 是否返回 AddRef 的IUnknown实例。本质上归结为以下哪一项是正确的

示例 1:

CComPtr<IUnknown> spUnk;
hr = SafeArrayGetElement(pArray, &bounds, reinterpret_cast<void**>(&spUnk));

示例 2:

IUnknown* pUnk;
hr = SafeArrayGetElement(pArray, &bounds, reinterpret_cast<void**>(&pUnk));

关于这个主题的文档非常薄。它仅包括以下行。

如果数据元素是字符串、对象或变量,则函数以正确的方式复制元素。

正确的定义有点模棱两可。

4

2 回答 2

2

第一种方法应该是正确的,并且与整个 COM 中对象的处理一致,大概您找到的定义假设消费者知道正确的方法。

提到的其他项目需要它。复制 VARIANT 或 SAFEARRAY 会在它们包含对象时携带隐式 AddRef()。但是,当 VT_BYREF 存在时,VARIANT 不需要它。

VariantCopy @ MSDN
SafeArrayCopy @ MSDN

此行为不是 SAFEARRAY 或 VARIANT 所固有的,因为它是 COM 中处理参数规则的一部分。不过,没有什么能阻止某人试图规避规则。

对于输入参数,AddRef() 的被调用者不负责,除非他们打算保留接口指针以供进一步使用。但是,其他使用参数的情况需要它。

例如,放置在 VARIANT 或其他容器中的接口应至少应用一个 AddRef() 调用,否则在使用 VARIANT 作为 COM 方法的输出参数时会产生问题,因为数据/引用的传输是单向的。原始对象可能会在调用到达其目的地时过期。同样,将接口编组到 Stream 也需要 AddRef()。

同样,通过引用调用也需要至少一个 AddRef 调用。如果不是这种情况,那么任何合适的长时间运行的调用(例如,通过 DCOM)可能无法在保证引用对象仍然存在的情况下到达其目的地。但是,这里经常跳过额外的 AddRef()/Release() 调用,因为由于在调用范围内或之前创建,对象应该已经处于 1+。

如果可以修改组件,并且您的调用正在进行中,那么可能需要改用 GIT。这允许您改为传递令牌,并且更容易在 COM 单元之间编组接口。所涉及的对象的生命周期在整个调用期间成为调用者的责任,并且您将能够捕获无法编组对象的情况。

创建全局接口表@MSDN

同样有趣的是 BSTR 的脚注。

如果采用 BSTR 引用参数的函数的实现将新的 BSTR 分配给参数,则它必须释放先前引用的 BSTR。

字符串操作函数 (COM)

于 2009-10-22T20:32:31.550 回答
1

它应该是 AddRef:ed,但我没有第一手信息就是这种情况(例如,我没有阅读源代码)。

不过,我认为文档很清楚——“正确”复制接口指针就是 AddRef:ing 它。

如果你想确定的话,构建一个超级简单的 ATL COM 对象,它实现IUnknown,将其中的一些填充到 a 中SAFEARRAY并在其中放置一个断点CComObjectBase<>::InternalAddRef(如果我没记错的话)。然后调试一个调用SafeArrayGetElement,看看你的断点是否被命中。

于 2009-10-22T18:41:46.500 回答