Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Destructors not called when native (C++) exception propagates to CLR component

We have a large body of native C++ code, compliled into DLLs.

Then we have a couple of dlls containing C++/CLI proxy code to wrap the C++ interfaces.

On top of that we have C# code calling into the C++/CLI wrappers.

Standard stuff, so far.

But we have a lot of cases where native C++ exceptions are allowed to propagate to the .Net world and we rely on .Net's ability to wrap these as System.Exception objects and for the most part this works fine.

However we have been finding that destructors of objects in scope at the point of the throw are not being invoked when the exception propagates!

After some research we found that this is a fairly well known issue. However the solutions/ workarounds seem less consistent. We did find that if the native code is compiled with /EHa instead of /EHsc the issue disappears (at least in our test case it did). However we would much prefer to use /EHsc as we translate SEH exceptions to C++ exceptions ourselves and we would rather allow the compiler more scope for optimisation.

Are there any other workarounds for this issue - other than wrapping every call across the native-managed boundary in a (native) try-catch-throw (in addition to the C++/CLI layer)?

like image 254
philsquared Avatar asked Mar 23 '10 18:03

philsquared


2 Answers

Unfortunately no I do not believe there are any good workarounds. The C++ exception implementation on MS platforms are implemented using SEH exceptions (IIRC). The CLR hooks into SEH handling to catch native exceptions and process them into CLR exceptions. Since it catches them at an SEH level the exception looks like an SEH exception to C++ and destructors are run or not run accordingly.

So as you've noted the best two options are

  • Compile with /EHa
  • Add a try/catch at the entry and exit point of your functions

Ideally you should be doing the second one anyways. In my experience it's considered bad practice to allow C++ exceptions to cross component boundaries.

There is also likely a hacky solution you could achieve using _set_seh_translator (Documentation). However I highly recommend avoiding that function as it can inadventently subvert CLR exception handling and cause a lot of unwanted problems.

like image 97
JaredPar Avatar answered Nov 06 '22 08:11

JaredPar


You are not doing this right I think. Using _set_se_translator() already requires you to compile with /EHa. From the MSDN Library page:

You must use /EHa when using _set_se_translator.

More seriously, you'll break managed code when you use it. The CLR relies on SEH exceptions to detect various mishaps. It uses SetUnhandledExceptionFilter to trap them. Particularly NullReferenceException and OverflowException (x86) are raised this way. When you inject your own __try block, you'll prevent this exception from flowing into the CLR. Although you'll "handle" it, you won't have any trace of the exact reason for the exception. And managed try/catch blocks can't detect it.

A cure for /EHa efficiency (if it is actually a problem) is 64-bit code. Its function table based stack unwinding is very efficient with zero overhead for a try block.

like image 24
Hans Passant Avatar answered Nov 06 '22 08:11

Hans Passant