我们在代码中使用Native COM支持。一切都很好,除了我们不喜欢在调用错误_com_raise_error()时抛出_com_error异常的事实。由于我们拥有自己的异常层次结构,因此捕获此_com_error很不方便 - 它不在我们的层次结构中,甚至不会从std :: exception继承。
所以我们需要覆盖_com_raise_error()。它本身很容易 - 只需在我们的代码中定义它,链接器就会与它链接。
但不清楚谁拥有IErrorInfo。签名是
void __stdcall _com_raise_error( HRESULT hr, IErrorInfo* info );
所以无论谁调用该函数都将负责在函数返回后调用IErrorInfo :: Release()。但是如果我们在其中抛出异常并且控件将转移到其他地方,该函数将如何返回?
我检查 - 调用AddRef(),然后在进入该函数后立即调用Release() - 引用计数器为1.稍后我们将所有权传递给构造的异常对象 - 它在其构造函数中调用AddRef()并释放()在析构函数中。我认为这是不正确的,因为AddRef()会将引用计数增加到2,但是只会调用一个Release()(在异常析构函数中)。
我是否认为构造函数中的AddRef()会导致内存泄漏,或者是否存在一些不允许IErrorInfo对象泄漏的内部机制?
答案 0 :(得分:2)
_com_raise_error()
无意返回。它必须引发异常,无论其类型如何。如果查看_com_raise_error()
的默认实现,则引发的_com_error
对象将获取指定的IErrorInfo对象的所有权。 _com_error
的构造函数有一个fAddRef参数,其默认值为false,因此不会调用AddRef()。然后,当任何异常处理程序捕获_com_error
对象时,将调用Release(),从而释放IErrorInfo对象。
答案 1 :(得分:1)
我认为_com_raise_error
会调用SetErrorInfo
,并将IErrorInfo
对象传递给它。对此的合同是对信息的引用存储在本地线程中,因此无论何时设置新信息,都会释放旧信息。此外,每当有人事后致电GetErrorInfo
时,信息的所有权就会转移给该来电者。因此,调用者有义务在每次可能设置失败的调用之后调用GetErrorInfo
,并相应地释放对象。
因此,SetErrorInfo
(与任何其他常规COM调用一样)将在您的对象上调用AddRef
,因此您不能使用计数器1来初始化它。
答案 2 :(得分:1)
添加其他答案,这里有几点想法:
一般的COM规则是in-parameters在任何级别都不需要是AddRef:ed,因为调用是同步的,并且在方法运行时引用计数不能神奇地改变。
< / LI>每个AddRef调用都代表一个对该对象的新稳定引用,也就是说,在调用AddRef之后,您可以指望仍在那里的对象。这意味着,如果您想存储一个接口指针供以后阅读,您应该调用AddRef。当您不再关心对象的生存时,请致电Release。
因此,既然你想抛出一个包含IErrorInfo指针的异常对象,那么该对象应该AddRef它,因为它需要指向对象才能生存。它的析构函数通常会释放。
我认为SetErrorInfo不需要参与其中 - 它是抛出异常的C替代方法。