Sample Reflection.Emit code for using exception filters from C#

Sample Reflection.Emit code for using exception filters from C#

In this post, I mentioned that one way to use exception filters from C# code is to generate them with Reflection.Emit. Personally I usually prefer static compilation (even post-build assembly merging or rewriting) – there’s really nothing here that necessitates dynamic code generation, but I can understand the desire to avoid complicating the build process. I recently wrote up some code to do this and figured I’d share it here in case others find it useful.

namespace FilterTest {///<summary>/// This class provides some utilities for working with exceptions and exception filters.///</summary>///<remarks>/// Code inside of exception filters runs before the stack has been logically unwound, and so the throw point /// is still visible in tools like debuggers, and backout code from finally blocks has not yet been run./// See http://blogs.msdn.com/rmbyers/archive/2008/12/22/getting-good-dumps-when-an-exception-is-thrown.aspx./// Filters can also be used to provide more fine-grained control over which exceptions are caught. ////// Be aware, however, that filters run at a surprising time - after an exception has occurred but before/// any finally clause has been run to restore broken invariants for things lexically in scope. This can lead to /// confusion if you access or manipulate program state from your filter. See this blog entry for details/// and more specific guidance: http://blogs.msdn.com/clrteam/archive/2009/08/25/the-good-and-the-bad-of-exception-filters.aspx.////// This class relies on Reflection.Emit to generate code which can use filters. If you are willing to add some/// complexity to your build process, a static technique (like writing in VB and use ILMerge, or rewriting with CCI)/// may be a better choice (eg. more performant and easier to specialize). Again see/// http://blogs.msdn.com/rmbyers/archive/2008/12/22/getting-good-dumps-when-an-exception-is-thrown.aspx for details.///</remarks>publicstaticclassExceptionUtils {///<summary>/// Execute the body with the specified filter.///</summary>///<param name="body">The code to run inside the "try" block</param>///<param name="filter">Called whenever an exception escapes body, passing the exeption object. /// For exceptions that aren't derived from System.Exception, they'll show up as an instance of RuntimeWrappedException.</param>///<param name="handler">Invoked (with the exception) whenever the filter returns true, causes the exception to be swallowed</param>publicstaticvoid ExecuteWithFilter(Action body, Func<Exception, bool> filter, Action<Exception> handler) { s_filter(body, filter, handler); }

///<summary>/// Execute the body with the specified filter with no handler ever being invoked///</summary>///<remarks>/// Note that this allocates a delegate and closure class, a small amount of overhead but something that may not be appropriate/// for inside of a tight inner loop. If you want to call this on a very hot path, you may want to consider a direct call/// rather than using an anonymous method.///</remarks>publicstaticvoid ExecuteWithFilter(Action body, Action<Exception> filter) { ExecuteWithFilter(body, (e) => { filter(e); returnfalse; }, null); }

///<summary>/// Execute the body which is not expected to ever throw any exceptions./// If an exception does escape body, stop in the debugger if one is attached and then fail-fast.///</summary>publicstaticvoid ExecuteWithFailfast(Action body) { ExecuteWithFilter(body, (e) => { System.Diagnostics.Debugger.Log(10, "ExceptionFilter", "Saw unexpected exception: " + e.ToString());

// Terminate the process with this fatal errorif (System.Environment.Version.Major >= 4) {// .NET 4 adds a FailFast overload which takes the exception, usefull for getting good watson buckets// This will also cause an attached debugger to stop at the throw point, just as if the exception went unhandled.// Note that this code may be compiled against .NET 2.0 but running in CLR v4, so we want to take advantage of// this API even if it's not available at compile time, so we use a late-bound call.typeof(System.Environment).InvokeMember("FailFast",BindingFlags.Static | BindingFlags.InvokeMethod,null, null, newobject[] { "Unexpected Exception", e }); }else {// The experience isn't quite as nice in CLR v2 and before (no good watson buckets, debugger shows a // 'FatalExecutionEngineErrorException' at this point), but still deubggable. System.Environment.FailFast("Exception: " + e.GetType().FullName); }

returnfalse; // should never be reached }, null); }

///<summary>/// Like a normal C# Try/Catch but allows one catch block to catch multiple different types of exceptions.///</summary>///<typeparam name="TExceptionBase">The common base exception type to catch</typeparam>///<param name="body">Code to execute inside the try</param>///<param name="typesToCatch">All exception types to catch (each of which must be derived from or exactly TExceptionBase)</param>///<param name="handler">The catch block to execute when one of the specified exceptions is caught</param>publicstaticvoid TryCatchMultiple<TExceptionBase>(Action body, Type[] typesToCatch, Action<TExceptionBase> handler)where TExceptionBase:Exception {// Verify that every type in typesToCatch is a sub-type of TExceptionBase#if DEBUGforeach(var tc in typesToCatch)Debug.Assert(typeof(TExceptionBase).IsAssignableFrom(tc), String.Format("Error: {0} is not a sub-class of {1}", tc.FullName, typeof(TExceptionBase).FullName));#endif

ExecuteWithFilter(body, (e) => {// If the thrown exception is a sub-type (including the same time) of at least one of the provided types then// catch it.foreach (var catchType in typesToCatch)if (catchType.IsAssignableFrom(e.GetType()))returntrue;returnfalse; }, (e) => { handler((TExceptionBase)e); }); }

///<summary>/// A convenience method for when only the base type of 'Exception' is needed.///</summary>publicstaticvoid TryCatchMultiple(Action body, Type[] typesToCatch, Action<Exception> handler) { TryCatchMultiple<Exception>(body, typesToCatch, handler); }///<summary>/// Set to true to write the generated assembly to disk for debugging purposes (eg. to run peverify and/// ildasm on in the case of bad codegen).///</summary>privatestaticbool k_debug = false;

// Will get generated (with automatic locking) on first use of this classprivatestaticAction<Action, Func<Exception, bool>, Action<Exception>> s_filter = GenerateFilter(); } }

There’s also a few helper methods there for common uses of exception filters. For example, to call code that you don’t expect to ever throw an exception you can just wrap it with ExecuteWithFailFast. If any exceptions escape it’ll immediately fail fast with a watson report and minidump (at the point of throw), or if a debugger is attached break at the throw point. The experience is better if running on CLR v4 – it makes use of the new FailFast API that takes an exception (eg. this will cause the debugger to break with the exception assistant, just as if the exception had gone unhandled). For example you can use this as follows:

Sometimes it’s useful to be able to catch multiple distinct exception types with the same catch block (without unwinding the stack for other exceptions, so unexpected exceptions are easier to debug live or in a dump file). Here are two examples: