我有一个COM函数,它应该通过LPSAFEARRAY*
输出参数返回一个SafeArray。
该函数使用ATL的CComSafeArray
模板类创建SafeArray。
我的天真实现使用CComSafeArray<T>::Detach()
来将所有权从局部变量移动到输出参数:
void foo(LPSAFEARRAY* psa)
{
CComSafeArray<VARIANT> ret;
ret.Add(CComVariant(42));
*psa = ret.Detach();
}
int main()
{
CComSafeArray<VARIANT> sa;
foo(sa.GetSafeArrayPtr());
std::cout << sa[0].lVal << std::endl;
}
问题是CComSafeArray::Detach()
执行Unlock
操作,以便在销毁SafeArray的新所有者(在这种情况下为main sa
)时,锁定不为零且{ {1}}无法使用Destroy
解锁SafeArray(由于SafeArray未解除分配,因此导致内存泄漏)。
通过COM方法边界将所有权转移到CComSafeArrays的正确方法是什么?
编辑:到目前为止,从单个答案看来,错误似乎是客户端(E_UNEXPECTED
),而不是来自服务器端(main
),但是我发现很难相信foo
不是为这个琐碎的用例而设计的,必须有一种优雅的方法可以将COM方法中的SafeArray转换为CComSafeArray
。
答案 0 :(得分:10)
问题是你直接设置了接收CComSafeArray
的内部指针。
使用Attach()
方法将现有SAFEARRAY
附加到CComSafeArray
:
LPSAFEARRAY ar;
foo(&ar);
CComSafeArray<VARIANT> sa;
sa.Attach(ar);
答案 1 :(得分:5)
只是为了确认标记的答案是正确的答案。 RAII包装器无法跨越COM边界工作。
发布的方法实现不正确,您不能假设调用者将提供有效的SAFEARRAY。只是[out]不是Automation中的有效属性,它必须是[out,retval]或[in,out]。如果它是[out,retval],它看起来像,那么该方法必须从头开始创建一个新数组。如果它是[in,out],则该方法必须销毁传入的数组,如果它与预期的数组类型不匹配并创建一个新数组。
答案 2 :(得分:1)
我猜想哪里不打算允许这样的用例。可能是编写CComVariant
&amp; CComPtr
:)
我认为CComSafeArray
的作者认为价值语义是主要目标;附加/分离可能只是一个“奖励”功能。