Visual Studio 2005 brings with it a new technology -- Just My Code. One nice feature that comes with Just My Code is the ability to stop on exceptions that you care about, without stopping on all exceptions. Usually this just works for you because something in a library that you are using (say WinForms), is eating all the exceptions that you want to stop on. Since your code didn't handle the exception, the debugger will stop.
However, sometimes your program might want to be able to tell the debugger when to stop. It turns out that it is possible to do this by using IL exception filters. With exception filters, you can divide the world into critical exceptions (which you want to stop on), and expected exceptions (which you don't want to stop on).
I think its easiest to show this with an example. Suppose you have some C# code, and you always want to stop on NullReferenceExceptions, but you don't actually care about anything else. Here is how you could do this.
enum DebuggerExceptionDisposition
{
DebuggerStop,
DebuggerIgnore
};
delegate void UserRoutine();
delegate DebuggerExceptionDisposition DebuggerExceptionFilter(Exception e);
[DebuggerNonUserCode]
static class ExceptionBoundry
[System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.ForwardRef)]
static public extern Exception Invoke(UserRoutine routine, DebuggerExceptionFilter filter);
class Program
static void Main(string[] args)
UserRoutine userRoutine = delegate()
//throw new ArgumentException();
throw new System.IO.FileNotFoundException();
DebuggerExceptionFilter filter = delegate(Exception e)
if (e is NullReferenceException)
return DebuggerExceptionDisposition.DebuggerStop;
}
return DebuggerExceptionDisposition.DebuggerIgnore;
try
ExceptionBoundry.Invoke(userRoutine, filter);
catch
The idea behind ExceptionBoundry.Invoke is that non-user code that will catch any exception that 'filter' tells it to catch (by returning DebuggerStop).
Here is the implementation for ExceptionBoundry.Invoke:
.class private abstract auto ansi sealed beforefieldinit cs.ExceptionBoundry extends [mscorlib]System.Object{ .custom instance void [mscorlib]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) .method public hidebysig static class [mscorlib]System.Exception Invoke(class cs.UserRoutine routine, class cs.DebuggerExceptionFilter 'filter') cil managed { .maxstack 2 .locals init ([0] class [mscorlib]System.Exception e)
.try { ldarg.0 callvirt instance void cs.UserRoutine::Invoke() leave.s EOF } // end .try filter { castclass class [mscorlib]System.Exception stloc.0 ldarg.1 ldloc.0 callvirt instance valuetype cs.DebuggerExceptionDisposition cs.DebuggerExceptionFilter::Invoke(class [mscorlib]System.Exception) ldc.i4.0 ceq endfilter } { leave.s EOF } // end handler EOF: ldloc.0 // return the first local ret } // end of method ExceptionBoundry::Invoke
} // end of class cs.ExceptionBoundry
To add it to your program, you could:
To disassemble your program:"<Visual Studio Directory>\SDK\v2.0\Bin\ildasm.exe" ..\cs\bin\debug\cs.exe /out=program.il /linenum
To assemble your program:C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\ilasm.exe /quiet /exe /debug=impl /OUTPUT=..\ILExceptionFilter.exe program.il