11

我有一个 COM 函数,它应该通过LPSAFEARRAY*out 参数返回一个 SafeArray。CComSafeArray该函数使用 ATL 的模板类创建 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在这种情况下为 main)被破坏时,锁不为零并且Destroy无法解锁 SafeArray E_UNEXPECTED(这会导致内存泄漏,因为 SafeArray 不是解除分配)。

通过 COM 方法边界在 CComSafeArrays 之间转移所有权的正确方法是什么?


编辑:从目前的单一答案看来,错误出在客户端(main)而不是服务器端(foo),但我很难相信这CComSafeArray不是为这个琐碎的用例设计的,必须有将 SafeArray 从 COM 方法转换为CComSafeArray.

4

3 回答 3

11

问题是您CComSafeArray直接设置了接收的内部指针。使用该Attach()方法将现有附加SAFEARRAY到 a CComSafeArray

LPSAFEARRAY ar;
foo(&ar);
CComSafeArray<VARIANT> sa;
sa.Attach(ar);
于 2009-11-22T12:21:11.743 回答
5

只是为了确认标记的答案是正确的。RAII 包装器不能跨 COM 边界工作。

发布的方法实现不正确,您不能假设调用者将提供有效的 SAFEARRAY。Just [out] 在 Automation 中不是有效属性,它必须是 [out,retval] 或 [in,out]。如果它是 [out,retval],这就是它的样子,那么该方法必须从头开始创建一个新数组。如果它是 [in,out],那么如果传入的数组与预期的数组类型不匹配,则该方法必须销毁传入的数组并创建一个新数组。

于 2013-08-06T03:35:21.130 回答
1

我猜想在哪里不允许这样的用例。可能不是同一位开发人员编写CComVariant& CComPtr:)

我相信CComSafeArray's 的作者认为价值语义是主要目标;附加/分离可能只是一个“奖励”功能。

于 2010-08-04T22:43:40.883 回答