Reputation: 25704
I have several dozen objects exposed through COM interfaces, each of which with many methods, totaling a few hundred methods. These interfaces expose business objects from my app to a scripting engine.
I have been given the task of protecting every single one of these methods from exceptions being thrown (to catch them and return an error using COM's Error()
function, which incidentally I can find no documentation on because it's impossible to google). To my understanding, this requires that I add a try/catch around the guts of each one of these methods. The catch blocks are going to be similar or identical for each and every one of these hundreds of methods, which strongly smells of a problem (massively violates the DRY principle), but I can't think of any way to avoid changing every method. As far as I can tell, these methods are invoked directly by COM, with no intervening code that I can hook into to catch the exceptions. My current best idea is to make a macro for the catch block, but that has it's own sort of code-smell. Can anyone come up with a better approach?
BTW, my app's exceptions do not derive from std::exception, so if there is some way of COM automatically handling standard exceptions, it won't help. And I sadly cannot change the existing exceptions to derive from std::exception.
Upvotes: 3
Views: 475
Reputation: 170489
The most reliable C++ way is to use macros here. I' ready to accept downvotes for saying this, but we've been using this solution for years and haven't seen any serious problems so far.
Define a "begin method" macro for clearing IErrorInfo
and try {
and "end method" for } catch
and the error handling. If you design the macros right - put all except the most necessary error handling code into helper functions it will be a tolerable and reliable solution with clean-looking moderately maintainable code.
Yes, that doesn't look good at all, but at least it is reliable and standard-compliant way of achieving what you want.
Upvotes: 3
Reputation: 941585
No happy answers here. It is illegal to let a C++ or SEH exception terminate a COM method. You must catch them and translate them to an appropriate HRESULT. Your coclass must implement the ISupportsErrorInfo and IErrorInfo interfaces so that the client can get exception information back. The CLR will readily do so to produce a customized exception message.
The Error() method you speak of is most likely the ATL CComCoClass::Error() method. It sets up the exception info that IErrorInfo will return.
There is no mechanism to inject a single try/catch block to catch all possible exceptions, COM methods are called directly from the client. You have to do so for each individual COM method that calls C++ code that can throw an exception. Unpleasant, but this should have been done when the code was written. You probably need to wrap every single one of them since finding out if it is calling code that may throw is difficult right now.
Technically, you could change the class factory to create a wrapper instead, one that implements each method of the interface and delegates to the actual method. With boilerplate try/catch handlers. Very mechanical, you can probably come up with some macros to lessen the blow.
Upvotes: 3
Reputation: 54604
Given that all COM interface methods use the __stdcall
convention and assuming you can hook into creating the COM objects yourself, you can "simply" create a wrapper COM object that returns a wrapped COM interface through QueryInterface
. This wrapped COM interface should be a COM interface where every vtable entry is replaced by a thunk that wraps the original vtable function with try/catch
.
I don't actually recommend doing this, but this is one way you can insulate the runtime implementing the COM object from external systems.
Upvotes: 0