views:

344

answers:

3

We use Native COM support in our code havily. Everything's fine except that we don't like the fact that on error _com_raise_error() is called which throws a _com_error exception. Since we have our own hierarchy of exceptions catching this _com_error is inconvenient - it's not in our hierarchy and even doesn't inherit from std::exception.

So we need to override the _com_raise_error(). It's easy by itself - just define it in our code, the linker will link with it.

However is unclear who owns the IErrorInfo. The signature is

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

So whoever calls the function would be responsible for calling IErrorInfo::Release() after the function returns. But how would the function return at all if we throw an exception in it and the control would transfer somewhere else?

I checked - called AddRef(), then Release() immediately upon entry into that function - the reference counter is 1. Later we pass ownership to the constructed exception object - it calls AddRef() in its constructor and Release() in destructor. I suppose this is incorrect since the AddRef() will increase the reference count to 2 but then only one Release() will be called (in the exception destructor).

Am I correct that the AddRef() in the constructor will cause a memory leak or is there some internal mechanism that doesn't allow IErrorInfo objects to leak at all?

A: 

I would imagine that _com_raise_error will call SetErrorInfo, passing it your IErrorInfo object. The contract for that is that the reference to the info is stored in a thread local, so whenever you set a new info, old one is released. Furthermore, whenever someone calls GetErrorInfo afterwards, ownership of info is transferred to that caller. So it's caller's obligation to call GetErrorInfo after every failed call that may set it, and release the object accordingly.

Thus, SetErrorInfo (as any other conventional COM call) will call AddRef on your object, so you do must not initialize it with a counter of 1.

Pavel Minaev
A: 

_com_raise_error() is not meant to return. It must raise an exception, regardless of its type. If you look at the default implementation of _com_raise_error(), the raised _com_error object takes ownership of the specified IErrorInfo object. _com_error's constructor has an fAddRef parameter that has a default value of false, so AddRef() is not called. Release() is then called when the _com_error object is destructed by whatever exception handler catches it, thus freeing the IErrorInfo object.

Remy Lebeau - TeamB
A: 

Adding to the other answers, here are a couple of thoughts:

  • The general COM rule is that in-parameters do not need to be AddRef:ed at any level, since calls are synchronous and the reference count can't magically change while the method runs.

  • Every AddRef call represents a new stable reference to the object, that is, after calling AddRef, you can count on the object still being there. That means, if you want to store an interface pointer for later perusal, you should call AddRef. When you no longer care about the object's survival, call Release.

So, since you want to throw an exception object containing an IErrorInfo pointer, that object should AddRef it, since it needs the pointed-to-object to survive. Its destructor would typically Release.

I don't think SetErrorInfo needs to be involved in this -- it's the C alternative to throwing an exception.

Kim Gräsman