Click here to Skip to main content
15,867,937 members
Articles / Programming Languages / C#

Capturing Unhandled Exceptions in a Mixed Native/CLR Environment

Rate me:
Please Sign up or sign in to vote.
5.00/5 (1 vote)
26 Aug 2014Apache5 min read 12K  
Capturing unhandled exceptions in a mixed native CLR environment

Observing Unhandled Exceptions

In .NET, unhandled exceptions can be observed via AppDomain.UnhandledException event:

C#
AppDomain.CurrentDomain.UnhandledException += myHandler; <font color="darkgreen">// C#</font>

In Win32, unhandled exceptions are observed via SetUnhandledExceptionFilter call:

C#
SetUnhandledExceptionFilter(myfilter); <font color="darkgreen">// C/C++</font>

Is Native or Managed Exception Filter Called First?

In a mixed native/managed environment, it is possible to setup both filters, which begs the question which filter would be called first, and whether the second filter would be called at all.

The answer to this question is complicated and does not seem to be documented anywhere. The information below is for .NET Framework 4.0 and 4.5.

If a managed exception occurs in the main (first) thread of the application, native handler is called first. If native handler was not set, or if it returns EXCEPTION_CONTINUE_SEARCH, managed handler is called second. Otherwise, managed handler is not called at all.

If a managed exception occurs in any other thread, including secondary UI threads, managed handler is called first, followed by native handler.

If a managed or native debugger is attached, managed handler is called first, and native handler is not called at all, for all threads. This makes the situation especially treacherous, since the behavior with and without debugger is different for main thread.

I am not sure whether this behavior is a bug or a feature. When initialized, CLR installs its own Win32 exception filter implemented by the CLRUEFManager class in mscoree.dll. “UEF” apparently stands for Unhandled Exception Filter. This class decides when to call managed unhandled exception logic and when to call “old” user-installed Win32 exception filter. To the best of my knowledge, the source code of this class is not public, and the reason why it works the way it works is not publicly known.

Strategy for Reliably Observing Unhandled Exceptions

If your application is mixed (managed/native), and the native part installs a Win32 unhandled exception filter, reliably observing managed unhandled exception from the main thread becomes a problem. Win32 UEF filter typically returns EXCEPTION_EXECUTE_HANDLER, because it has no reason to do otherwise. In fact, in our production application, it went even further and called TerminateProcess(). For main thread Win32 filter is called first, and thus there is a very high chance that managed UnhandledException event will not be called.

This problem does not exist for secondary threads, because the managed exception filter typically does not terminate the application in its body, so both filters will be called as expected.

You can try to prevent unhandled exceptions altogether by putting try/catch on every managed/unmanaged boundary. However, I would not call this strategy reliable. It is complicated if you have large body of code, and pretty much impossible if you deal with mixed third party libraries.

Another strategy is to ensure Win32 unhandled exceptions filter always returns EXCEPTION_CONTINUE_SEARCH. This may be problematic if you deal with third party libraries that install their own UEF filters. However, even in this case, not all is lost: after all, we are in the native world where nearly everything is possible (but nothing of interest is easy). For instance, you could override SetUnahdneldExceptionFilter() call with Detours and make sure you keep the top-level handler that always returns EXCEPTION_CONTINUE_SEARCH.

For WPF applications, one can use Dispatcher.UnhandledException event. It will be correctly called before the native exception filter, even on main thread. In Windows Forms applications, Application.ThreadException plays similar role. So, for those types of applications, a hybrid approach would work: handle main thread managed exceptions in Dispatcher.UnhandledException, and all others in AppDomain.CurrentDomain.UnhandledException. If there is a chance that native UEF may return EXCEPTION_CONTINUE_SEARCH, the same exception may be passed to both Dispatcher and AppDomain events, so care must be taken not to do things twice in this case.

Application Samples and Use Cases

Mixed.Exceptions.zip contains sample code that supports the above assertions. It has three folders:

  • ClrHosting.Exceptions.Console
  • ClrHosting.Exceptions.Wpf
  • Exceptions.Console.ManagedC++

ClrHosting.Exceptions.Console

This is a console application that hosts its own CLR. There are two projects in the solution:

  • ClrHosting: Native C++ application
  • ManagedCode: Managed C# DLL

Native application calls managed code via <a href="http://msdn.microsoft.com/en-us/library/ms164411%28v=vs.110%29.aspx">ICLRRuntimeHost::ExecuteInDefaultAppDomain</a> method. If an exception occurs inside the call, managed exception handler is not called. Instead, ExecuteInDefaultAppDmain returns a failing HRESULT. It is COR_E_APPLICATION (0x80131600) if thrown exception was derived from System.ApplicationException, and COR_E_EXCEPTION (0x80131500) if it was not.

However, asynchronous exception in a worker thread does result in calling managed AppDomain.UnhandledException, followed by the native UEF handler.

The application throws exception in the main thread by default. To cause it to throw exception in a worker thread, pass it a command line argument, e.g. ClrHosting.exe x.

ClrHosting.Exceptions.Wpf

This solution is a variation of console CLR hosting, but here the managed code creates a WPF window and the native code runs a message loop. WPF window has buttons to throw an exception in the UI thread, in a worker thread, or create a window on a new UI thread.

Main difference between this example and console example is that main thread exceptions do not occur directly inside ExecuteInDefaultAppDomain() call. Instead, they happen inside DispatchMessage(), which is part of the message loop.

For main thread exceptions Dispatcher.UnhandledException is called first, then native UEF handler, followed by the AppDomain.UnhandledException event.

For worker thread exceptions and secondary UI thread exceptions, the picture is reverse: managed handler first, then native UEF handler.

Exceptions.Console.ManagedC++

This solution does not involve explicit CLR hosting. Instead, it applies a IJW (“it just works”) technique for calling managed C++ from native C++ and vice versa. It has two projects: NativeCppApp.exe and ManagedCppDLL.dll. The EXE loads the DLL dynamically and calls the managed entry point using GetProcAddress. At this point, the CLR is loaded implicitly into the process.

The behavior is pretty much the same as for the WPF application. For main thread native handler is called first, CLR handler is called second. For worker threads, CLR handler is called first, native handler second.

Conclusion

I am sure the .NET/CLR team had some reasons to implement unhandled exceptions filtering the way they did. It does not look like a random bug. However, I am completely in the dark as to what these reasons could be. I would prefer that every thread behaved like a worker thread and called the CLR handler first. If that’s not possible, I would have appreciated a decent documentation, but I could not find anything on the subject. Still, the behavior appears to be consistent, so at least we know what to expect now.

This article was originally posted at http://www.ikriv.com/blog?p=1440

License

This article, along with any associated source code and files, is licensed under The Apache License, Version 2.0


Written By
Technical Lead Thomson Reuters
United States United States
Ivan is a hands-on software architect/technical lead working for Thomson Reuters in the New York City area. At present I am mostly building complex multi-threaded WPF application for the financial sector, but I am also interested in cloud computing, web development, mobile development, etc.

Please visit my web site: www.ikriv.com.

Comments and Discussions

 
-- There are no messages in this forum --