如何将本地CComSafeArray返回到LPSAFEARRAY输出参数?

12

我有一个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的新所有者(在这种情况下为主函数中的sa)被销毁时,锁定计数不为零,导致Destroy无法通过E_UNEXPECTED解锁SafeArray(这会导致内存泄漏,因为SafeArray未被释放)。
如何正确地在COM方法边界之间传递两个CComSafeArray之间的所有权?
编辑:从迄今为止的单个答案来看,错误似乎出现在客户端(main)而不是服务器端(foo),但我很难相信 CComSafeArray没有为这种微不足道的用例设计,必须有一种优雅的方法将SafeArray从COM方法中提取到CComSafeArray中。

你正在使用哪个版本的Visual Studio? - Phil Booth
这对VS8(2005年)和VS9(2008年)都是发生的。 - Motti
2
根据我的经验,我认为设计CComSafeArray的人从未真正使用过它。如果您愿意,可以使用自己的包装类。 - Amnon
3个回答

11

问题在于你直接设置了接收的 CComSafeArray 的内部指针。 使用 Attach() 方法将一个现有的 SAFEARRAY 附加到一个 CComSafeArray 中:

LPSAFEARRAY ar;
foo(&ar);
CComSafeArray<VARIANT> sa;
sa.Attach(ar);

这肯定不是 CComSafeArray 应该被使用的方式,它违背了 CComVariantCComBSTR 的本意。 - Motti
正如您在代码中看到的那样,CComSafeArray期望SAFEARRAY被锁定。您必须以某种方式将其锁定。 - Amnon
而且没有类似于Attach的功能来锁定,也没有类似于Detatch的功能来解除锁定 - 因此工作必须在调用方或被调用方进行。 - Georg Fritzsche

5

确认标记答案为正确答案。RAII包装无法跨越COM边界工作。

发布的方法实现不正确,您不能假设调用者会提供有效的SAFEARRAY。仅使用[out]不是自动化中的有效属性,它必须是[out, retval]或[in,out]。如果是[out,retval],那么该方法必须从头创建一个新数组。如果是[in,out],则该方法必须销毁传入的数组(如果它不符合预期的数组类型)并创建一个新数组。


1

我猜这个用例不是有意允许的。可能编写CComVariantCComPtr的不是同一个开发人员 :)

我相信CComSafeArray的作者将值语义视为主要目标;Attach/Detach可能只是一个“奖励”功能。


1
即使有这个理由,我仍然觉得CComSafeArray的默认构造函数和GetSafeArrayPtr是设计缺陷/解决方法... - Andrey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接