This is a bug Dima encountered several months ago, and I’ve been looking for an opportunity to document ever since.
tl;dr – when unmanaged C++ code throws an exception which propagates through an interop boundary to managed code, C++ destructors are not called. To fix, don’t allow C++ exception propagation outside module boundaries, or compile with /EHa.
It was recorded before on this StackOverflow thread, where the conclusion is that “the CLR hooks into SEH handling to catch native exceptions and process them into CLR exceptions. […] the exception looks like an SEH exception to C++ and destructors are […] not run”.
There are several solutions, in increasing order of “recommendedness”:
- Do not allow C++ exceptions to propagate untouched across the interop boundary. This is generally a good idea, because C++ exception handling is really best handled by C++ code only, and allowing exceptions to propagate outside is dangerous in other settings as well (e.g. exporting a function from a DLL that throws a C++ exception). If this were an option, you wouldn’t be reading on, so here goes . . .
- Compile your C++ code with /EHa to indicate that SEH exceptions may be thrown from C++ code. This will limit compiler optimizations w.r.t. stack unwind and destructor invocation, which is exactly what we need in this scenario.
- Use the _set_se_translator function to translate SEH exceptions back to C++ exceptions (basically, just by throwing a C++ exception from the body of the function). This is a hack and probably expensive, performance-wise.
A few years ago I demonstrated the interaction between the CLR and SEH by installing my own unhandled exception filter. This is just another demonstration of how brittle the state of affairs is between managed and unmanaged code.
I am posting short updates and links on Twitter as well as on this blog. You can follow me: @goldshtn