Search code examples
c#.netvisual-studiotask-parallel-libraryvisual-studio-debugging

Selectively preventing the debugger from stopping on 1st chance exceptions


I know I can prevent the Visual Studio debugger from stopping on certain kind of exceptions when they're thrown (via the Ctrl-Alt-E "Exceptions" dialog). But what if want to control this from code, for some specific places rather than on the all-or-none basis? For example:

try
{
    SomeMethod(token);
}
catch (OperationCancelledException)
{
    return false;
}

// ...

void SomeMethod(CancellationToken token)
{
    // ...

    // I don't want the debugger to stop on the following line
    #pragma ignore(OperationCancelledException, true)
    token.ThrowIfCancellationRequested();
    #pragma ignore(OperationCancelledException, false)
} 

I use the hypothetic #pragma ignore to illustrate what I mean, but does something like this actually exist?

UPDATE to address "Unclear what you're asking" closure vote. Try this code in the debugger: https://dotnetfiddle.net/npMk6r. Make sure all exceptions are enabled in the Ctrl-Alt-E dialog. The debugger will be stopping on the throw new OperationCanceledException("cancelled1") line upon each iteration of the loop. I don't want that to happen as it's annoying. Yet, I do want it to stop on the last throw outside the loop, throw new OperationCanceledException("cancelled2") (or anywhere else, for that matter).


Solution

  • This might not be exactly what you're looking for, but I'd use DebuggerNonUserCode attribute for this.

    To illustrate that, here's a modified version of your fiddle. The debugger won't stop on ThrowIfCancellationRequested even though OperationCanceledException is enabled in the Ctrl+Alt+E Exceptions dialog.

    using System;
    using System.Diagnostics;
    using System.Threading;
    
    namespace TestApp
    {
        static class Ext
        {
            [System.Diagnostics.DebuggerNonUserCode()]
            public static bool TryThrowIfCancellationRequested(
                this CancellationToken token)
            {
                try
                {
                    // debugger won't stop here, because of DebuggerNonUserCode attr
                    token.ThrowIfCancellationRequested();
                    return true;
                }
                catch (OperationCanceledException)
                {
                    return false;
                }
            }
        }
    
        public class Program
        {
            static bool SomeMethod(CancellationToken token)
            {
                System.Threading.Thread.Sleep(1000);
                return token.TryThrowIfCancellationRequested();
            }
    
            public static void Main()
            {
                var cts = new CancellationTokenSource(1000);
    
                for (var i = 0; i < 10; i++)
                {
                    if (!SomeMethod(cts.Token))
                        break;
                }
            }
        }
    }
    

    Of course, you could have used CancellationToken.IsCancellationRequested instead of ThrowIfCancellationRequested in this particular case, but the above approach illustrates the concept which can be extended to any other exception.