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

风流意气都作罢 提交于 2019-12-03 01:13:33

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.

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.

The MSDN page for the /E compiler switch does state this behaviour:-

http://msdn.microsoft.com/en-us/library/1deeycx5(VS.80).aspx

Here is the relevant quote:-

If you use /EHs, then your catch clause will not catch asynchronous exceptions. Also, in Visual C++ 2005, all objects in scope when the asynchronous exception is generated will not be destroyed even if the asynchronous exception is handled.

Basically /EHsc is the optimistic view - it assumes that the only exceptions are true C++ style ones and will optimise accordingly. /EHa on the other hand takes the pessimistic view and assumes that any line of code could cause an exception to be generated.

If you can guarentee that you'll never cause an Access Violation, or In-Page Error or other SEH then use /EHsc. But If you're writing a service and/or want to provide a "best effort" then /EHa is going to be necessary.

I also agree with @JaredPar's sentiments about not allowing exceptions to cross module boundaries. What @nobugz says about the way the CLR handles exceptions may be true, but I think there is a difference between .Net code calling out directly to native code using P/Invoke and calling into a C++/CLI interop DLL. In the former case the CLR has to handle the situation on your behalf, whereas in the latter you are in control and can translate accordingly.

roken

There is a bug in the 2.0 version of the CLR causing this issue. Running your managed executable on the 4.0 CLR will allow the destructors to be called as expected.

See Boost shared mutex not released after exception thrown for details.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!