Catch exception if debugger is not attached
c#, debugging, vb.net, visual-studio
Solution
Exception Filters (C# 6+)
If you're using C# 6, this is easy to do with the new exception filter syntax:
try
{
DoSomething()
}
catch (Exception e) when (!System.Diagnostics.Debugger.IsAttached)
{
Console.WriteLine(exception.Message);
}
Problem
Desired behaviour (the question) In a C# application, what I would like is this: When the debugger is not attached: - - Exception is thrown. - Exception is caught higher up in the stack. - Log error and continue. When the debugger is attached: - - Exception is thrown. - Debugger breaks at the point where the exception is thrown. To illustrate by way of an example, here is how it might work with a conditional catch (I know that this is not supported in C#): Note: while I am showing an example of the exception being thrown by my code, it may be thrown by a 3rd party library. ``` static void DoSomething() { //This is where I would like the debugger to break execution and show the exception throw new Exception( "Something went wrong!" ); } static public void DoSomeStep() { try { DoSomething(); } catch( Exception exception when System.Diagnostics.Debugger.IsAttached == false ) //If the debugger is attached don't catch { Console.WriteLine( exception.Message ); //Do some processing on the exception } } static void Main( string[] args ) { for( int i = 0; i < 10; i++ ) { DoSomeStep(); } } ``` Background This is not a big issue, since there are stack traces and logging to piece the information together, but I was wondering if there is a good way of achieving this, since it comes up now and then (and is one of those thousand cuts that I wouldn't mind doing without). Also, I have never found an ideal method, so I am interested if there is one. It is particularly relevant in a program where there are a number of steps (such as running tests). During normal standalone operation, if any of these steps raise an exception an error should be logged and execution should move onto the next step. However, when running in the debugger the debugger should break at the point where the exception was raised. This will speed up the debugging process as you don't need to consult stack traces and the state of the local variables will be preserved. The rest of this question describes things that I have already tried so that they are not repeated in the answers... Approaches that have already been considered Conditional catch in VB DLL I know that this is not supported in C#, but it is supported in VB.NET. So, I can get the desired behaviour by implementing the following in a VB.NET library (don't worry about the code too much, it basically wraps a method in a `try...catch` and calls an error handler if there is an exception and the debugger is not attached): ``` Public Module DebuggerNoCatch Public Function Run(Of T, U, V, W, X)(func As Func(Of T, U, V, W, X, Boolean), arg1 As T, arg2 As U, arg3 As V, arg4 As W, context As X, errorHandler As Action(Of System.Exception, X)) As Boolean Dim result As Boolean = False Try result = func(arg1, arg2, arg3, arg4, context) Catch ex As Exception When Not Debugger.IsAttached errorHandler(ex, context) result = False End Try Return result End Function End Module ``` Note that there need to be different overloads for `Run` depending on the number of arguments (in this case mine just happens to use 4 args). Also, there is a `Context` parameter for the cases where some state needs to be maintained between the method being called and the error handler. Then my code looks something like this: ``` static bool DoSomething( int a, int b, int c, int d, RunContext context ) { //Now the debugger break at this point - hooray! throw new Exception( "Something went wrong!" ); return true; } static void HandleException( Exception exception, RunContext context ) { //Only see this when not attached in the debugger Console.WriteLine( exception.Message ); //Do some processing on the exception } class RunContext{ } //context information - not used in this example static public void DoSomeStep() { DebuggerNoCatch.Run<int, int, int, int, RunContext>( DoSomething, 1, 1, 1, 1, new RunContext(), HandleException ); } ``` The drawbacks of this approach are: - - The added complexity of another VB.NET DLL in the project. - Not as intuitive as a simple `try...catch` - other people coming to the code for the first time would need to dig around to understand exactly what is going on. Re-throw The code (note the `throw`): Example: ``` static public void DoSomeStep() { try { DoSomething(); } catch( Exception exception ) { Console.WriteLine( exception.Message ); //Do some processing on the exception //If the debugger is attached throw, otherwise just continue to the next step if( System.Diagnostics.Debugger.IsAttached == true ) { //This is where the debugger breaks execution and shows the exception throw; } } } ``` The problem with this is that while `throw` preserves the stack trace, the debugger breaks at the line where the throw occurs rather than the original throw. It makes complete sense that it happens in this way, but it is not what I want to happen. It means that I need to look inside the exception for the stacktrace and then find the correct line of code. Also, the state of the local variables where the exception occurred is lost. Wrapper method Basically, just wrap the `try...catch` in a separate method: ``` static void DoSomething() { //This is where I would like the debugger to break execution and show the exception throw new Exception( "Something went wrong!" ); } static void DoSomethingContinueOnError() { try { DoSomething(); } catch( Exception exception ) { Console.WriteLine( exception.Message ); //Do some processing on the exception } } static public void DoSomeStep() { if( System.Diagnostics.Debugger.IsAttached == false ) { DoSomethingContinueOnError(); } else { DoSomething(); } } ``` But, there are a number of problems with this: - More code. - Things quickly become unwieldy for more complicated cases, such as when there are more parameters or there are variables local to the `try...catch` that are set by need to be passed into 'DoSomething' by reference of if there are sub-steps. Conditional compilation symbols This is probably my least favourite option. In this case a conditional compilation symbol such as DEBUGGING is used (note DEBUG will not work because I may be running DEBUG without the compiler attached): ``` #if !DEBUGGING try #endif { DoSomething(); } #if !DEBUGGING catch( Exception exception ) { Console.WriteLine( exception.Message ); //Do some processing on the exception } #endif } ``` The problems are: - - This is all a bit of a headache to manage and I would invariably not have it set when I needed it. Specifically, the symbol and the fact that the debugger is attached are not related in any way other than by me manually setting the symbol definition. - `#DEBUGGING` clutters the code and makes the `try...catch` less readable. Other - Visual Studio setting. I have also investigated the different Visual Studio exception break settings, but I want to turn the behaviour on for specific parts of the code, not for specific exceptions. Also, this should work across installations. - Assembly IL. I have considered inline IL as an option to generate the conditional exception, but this requires post-build steps with 3rd party tools. - I don't think that global (application) exception handlers will do it because the exception needs to be caught and logged lower down up in the application stack. Update - DebuggerStepThrough and Re-throw Steven Liekens' comment indicates what seems to be a good solution - the `DebuggerStepThroughAttribute`. When this attribute is set on a method containing a re-throw, the debugger breaks at the original point of the exception, not where it is re-thrown as shown below: ``` static bool DoSomething() { //This is where the debugger now breaks execution throw new Exception( "Something went wrong!" ); return true; } [DebuggerStepThrough] static public void DoSomeStep() { try { DoSomething(); } catch( Exception exception ) { Console.WriteLine( exception.Message ); if( Debugger.IsAttached == true ) { //the debugger no longer breaks here throw; } } } static void Main( string[] args ) { for( int i = 0; i < 10; i++ ) { DoSomeStep(); } } ``` The only drawback is if you do actually want to step into the code marked as `DebuggerStepThrough` or if there is an exception in this code. Though, this is a minor drawback because you can generally keep this code minimal. Note the use of `Debugger.IsAttached` because I think its impact here is minimal and the likelihood of strange heisenbugs are minimal, but beware of using it as pointed out by Guillaume in the comments and use another option such as config setting when appropriate. I will go with this unless a better way or someone raises concerns about it.