2009-07-29 55 views
1

我们在代码中使用Native COM支持。一切都很好,只是我们不喜欢错误_com_raise_error()被调用,抛出_com_error异常的事实。由于我们有自己的异常层次结构捕获这个_com_error是不方便的 - 它不在我们的层次结构中,甚至不能从std :: exception继承。谁拥有IErrorInfo的所有权?

所以我们需要重写_com_raise_error()。它本身很简单 - 只需在我们的代码中定义它,链接器就会与它链接。

但是不清楚谁拥有IErrorInfo。签名是

void __stdcall _com_raise_error(HRESULT hr, IErrorInfo* info); 

所以无论谁调用该函数将负责函数返回后调用IErrorInfo :: Release()。但是如果我们抛出一个异常并且控制权会转移到其他地方,那么函数将如何返回呢?我们检查了所谓的AddRef(),然后在进入该函数时立即释放(),引用计数器为1.稍后,我们将所有权传递给构造的异常对象 - 它在其构造函数中调用AddRef() )在析构函数中。我想这是不正确的,因为AddRef()会将引用计数增加到2,但只有一个Release()将被调用(在异常析构函数中)。

我正确的说,构造函数中的AddRef()会导致内存泄漏,或者是否有一些内部机制不允许IErrorInfo对象泄漏?

回答

1

我想象一下_com_raise_error会打电话给SetErrorInfo,把它传递给IErrorInfo这个对象。与此相关的合同是对信息的引用存储在本地线程中,因此无论何时设置新信息,旧信息都会被释放。此外,每当有人以后拨打GetErrorInfo时,信息的所有权将转移给该来电者。因此,呼叫方有义务在每次可能设置呼叫失败后调用GetErrorInfo,并相应地释放该对象。

因此,SetErrorInfo(任何其他传统COM调用)将调用AddRef在对象上,这样你就不能与1

2

_com_raise_error()计数器并不意味着回到初始化。无论其类型如何,它都必须引发异常。如果您查看默认实现_com_raise_error(),则提出的_com_error对象将获取指定的IErrorInfo对象的所有权。 _com_error的构造函数具有默认值为false的fAddRef参数,因此不调用AddRef()。当_com_error对象被任何异常处理程序捕获时释放Release(),从而释放IErrorInfo对象。

1

添加到其他的答案,这里有一对夫妇的想法:

  • 一般的COM规则是,在参数并不需要是的AddRef:ED在任何级别,因为调用是同步的,并该方法运行时引用计数不会奇迹般地改变。

  • 每个AddRef调用代表一个新的稳定的对象引用,也就是说,在调用AddRef之后,你可以指望对象仍然在那里。这意味着,如果你想存储一个接口指针供以后阅读,你应该调用AddRef。当你不再关心物体的生存时,请致电Release。

所以,既然你想抛出包含的IErrorInfo指针异常对象,该对象应的AddRef它,因为它需要指向的对象生存。它的析构函数通常会释放。

我不认为SetErrorInfo需要参与此 - 这是抛出异常的C选择。