垃圾初始值是否可以用于[[out] BSTR *`参数?

时间:2013-06-07 11:26:56

标签: memory-management com bstr

根据MSDN

  

对于[out]参数,方法或属性分配内存和   调用者负责释放内存。

其中哪些都可以:

[...]
STDMETHOD(Method)([out] BSTR* psValue)
[...]

BSTR myBStr1;
Method(&myBStr1);
::SysFreeString(myBStr1);

BSTR myBStr2 = SysAllocString(L"MyStringValue");
Method(&myBStr2);
::SysFreeString(myBStr2);

BSTR myBStr3 = NULL;
Method(&myBStr3);
::SysFreeString(myBStr3);

2 个答案:

答案 0 :(得分:4)

是的,垃圾是可以接受的。该方法负责初始化该值。来电者只负责释放。

out attribute上的MSDN:

  

远程时,假定[out] -only参数未定义   调用过程并为该对象分配内存   服务器

#1,#3没问题。 #2会造成内存泄漏。

这条规则实际上造成了一些混乱。如果方法返回错误,您可能不确定值是否已初始化。如果您尝试释放垃圾,它将导致访问冲突或损坏内存。如果你跳过内存释放和部分成功的方法留下了一些有意义的东西(好吧,这是服务器端的问题,但仍然),那么你泄漏内存。在调用之前使用NULL进行初始化肯定更安全,例如,CComBSTR构造函数会为您执行此操作。在服务器端,您可能希望使用{out}值的NULL初始化来启动方法,以免在以后意外地将它们保留为未初始化。

答案 1 :(得分:3)

从编组调用的角度看规则(即,当对象位于不同的COM公寓中时,或者当它存在于不同的进程或机器中时会发生什么);你会理解规则是如何运作的,以及为什么它是唯一明智的规则。

关键的事实是,对于[out]参数,编组器将初始值编组为被调用者。

这意味着在拨打电话之前放入变量的内容并不重要。它可以是垃圾或NULL。没有人会去看它。

注意:这也意味着如果变量在调用之前包含需要释放的内容,则必须在调用之前自行释放它,否则它将被泄露。编组器不会为你释放它(因为作为参数[out]编组器必须假定它是垃圾)并且被调用者即使它想要也不能释放它(因为它永远不会从它获得现有的值) mashaller)。

其次,在调用返回后,被调用者无法知道你对返回值做了什么,所以唯一可以释放参数的是调用者。

错误条件的情况有点儿。

原则上,COM要求在返回之前必须将[out]参数设置为一致(“marshallable”?)值(NULL或等效或有效值),即使存在错误。这也是“唯一明智的规则”:编组人员无法知道被调用者处于错误状态。它也不会基于HRESULT进行猜测,因为即使是“错误类”HRESULT也不意味着该方法不打算将值返回给调用者。 marshaller会将参数的值编组回调用者 - 错误或没有错误 - 因此参数更有效,或者编组本身会崩溃。

在实践中......好吧,有些对象比其他对象编写得更好,有些COM类从不通过编组调用,因此类的开发人员从不会遇到问题。不符合这些规定的课程不能在公寓内安全使用。

即使面对错误情况,确保参数值保持一致的规范方法看起来像这样:

STDMETHODIMP Class::Method(BSTR *pValue)
{
  *pValue = NULL;        // 1) Initialize all the [out] parameters before
                         //    doing anything, to make sure they are always
                         //    consistent

  BSTR tempValue;        // 2) Use a temporary to hold any result value

  ...                    // 3) Use the temporary for all computations
  ...                    //    Finish all other processing

  *pValue = tempValue;   // 4) Only after all possible error conditions
                         //    are behind us, do put the new value
                         //    in the output parameter

  return S_OK;
}

显然,如果out参数只是一个不需要释放的值(比如int等),那么这一切都不重要。