我们有一个COM组件,其实现和接口定义存在于托管代码中,但由本机组件驱动。托管组件通过以下方法声明将SafeArray
返回到本机代码。
interface IExample {
<return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType=VarEnum.VT_UNKNOWN)>
object[] DoSomeOperation()
}
生成的本机签名正确地将其作为SafeArray
传回。
在代码审查期间,我们提出了一些关于使用SafeArrayGetElement调用结果数组的问题。问题是SafeArrayGetElement是否返回一个AddRef'd的IUnknown
实例。基本上它归结为以下哪一个是正确的
示例1:
CComPtr<IUnknown> spUnk;
hr = SafeArrayGetElement(pArray, &bounds, reinterpret_cast<void**>(&spUnk));
示例2:
IUnknown* pUnk;
hr = SafeArrayGetElement(pArray, &bounds, reinterpret_cast<void**>(&pUnk));
关于这个主题的文档很薄。它只包括以下行。
如果数据元素是字符串,对象或变体,则该函数以正确的方式复制元素。
正确的定义有点含糊不清。
答案 0 :(得分:2)
第一种方法应该是正确的,并且与整个COM中对象的处理一致,可能是你发现的定义假设消费者知道正确的方法。
提到的其他项目需要它。复制VARIANT或SAFEARRAY时,如果包含对象,则会带有隐式AddRef()。但是,当VT_BYREF存在时,VARIANT不需要它。
VariantCopy @ MSDN
SafeArrayCopy @ MSDN
此行为不是SAFEARRAYs或VARIANTs固有的,因为它是处理COM中参数的规则的一部分。但是,没有任何东西阻止某人试图规避规则。
对于输入参数,除非他们打算保留接口指针以供进一步使用,否则被调用者不负责AddRef()。但是,参数使用的其他情况需要它。
例如,置于VARIANT或其他容器中的接口应至少应用一个AddRef()调用,否则当使用VARIANT作为COM方法的输出参数时会产生问题,因为数据/引用的传输是单向的。原始对象可能在呼叫到达目的地时到期。同样,将接口封送到Stream中也需要AddRef()。
同样,通过引用调用也需要至少一个AddRef调用。如果不是这种情况,那么任何合适的长时间运行的呼叫(例如,通过DCOM)可能无法到达其目的地,并保证所引用的对象仍然存活。但是,此处经常会跳过额外的AddRef()/ Release()调用,因为在调用范围内或之前,对象应该已经为1+。
如果可以修改组件,并且您的呼叫正在进行中,则可能需要使用GIT。这允许您传递令牌,并且更容易编组COM公寓中的接口。在调用期间,所涉及对象的生命周期成为调用者的责任,并且您将能够捕获无法封送对象的情况。
Creating the Global Interface Table @ MSDN
同样有趣的是BSTR的脚注。
如果采用BSTR引用参数的函数的实现为参数分配了新的BSTR,它必须释放先前引用的BSTR。
答案 1 :(得分:1)
它应该是AddRef:ed,但我没有第一手资料(例如我没有阅读过来源)。
我认为文档非常清楚 - 正确地复制接口指针是AddRef:它。
如果你想确定,建立一个实现IUnknown
的超简单ATL COM对象,将其中的一些填充到SAFEARRAY
并在CComObjectBase<>::InternalAddRef
中放置一个断点(如果我的记忆服务)。然后调试对SafeArrayGetElement
的调用,看看你的断点是否被击中。