2009-11-22 45 views
11

我有一个COM函数应该返回一个SafeArray通过LPSAFEARRAY*出参数。 该函数使用ATL的CComSafeArray模板类创建SafeArray。 我天真的执行情况,以便从本地变量移动所有权的输出参数使用CComSafeArray<T>::Detach()如何将本地CComSafeArray返回到LPSAFEARRAY输出参数?

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操作,这样,当在这个安全数组(主要的sa的新主人情况)被破坏锁不是零,并且Destroy未能解锁SafeArray与E_UNEXPECTED(这导致内存泄漏,因为SafeArray未取消分配)。

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


编辑:从单一的答案到目前为止,似乎错误是在客户端(main),而不是从服务器端(foo),但我觉得很难相信CComSafeArray WASN不是为这个简单的用例设计的,必须有一个优雅的方法来将SafeArray从COM方法中取出,并将其转换为CComSafeArray

+0

您正在使用哪个版本的Visual Studio? – 2009-11-22 21:39:56

+0

这发生在VS8(2005)和VS9(2008) – Motti 2009-11-23 06:55:34

+2

基于我的经验,我相信设计CComSafeArray的人从来没有真正使用它。如果需要,您可以使用自己的包装类。 – Amnon 2009-11-25 13:16:52

回答

10

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

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

这肯定不是'CComSafeArray'应该被使用的方式,它违背了'CComVariant'和'CComBSTR'的粒度。 – Motti 2009-11-22 12:42:25

+0

正如你在代码中看到的,CComSafeArray期望SAFEARRAY被锁定。你必须以某种方式锁定它。 – Amnon 2009-11-22 18:01:30

+0

没有类似附件的功能可以锁定,也没有不能解锁的类似Detach的功能 - 所以工作必须在呼叫者或被呼叫者一方完成。 – 2009-11-22 21:23:01

1

我猜这里是无意允许这样的使用情况。也许这不是谁写CComVariant & CComPtr :)

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

+1

即使有这个理由,我仍然觉得'CComSafeArray'的默认ctor和'GetSafeArrayPtr'是设计缺陷/解决方法...... – Andrey 2010-08-04 22:44:15

5

只是为了确认标记的答案是正确的答案。 RAII包装无法在COM边界上工作。

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