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

时间:2009-11-22 11:43:10

标签: c++ com atl safearray

我有一个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

3 个答案:

答案 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的作者认为价值语义是主要目标;附加/分离可能只是一个“奖励”功能。