Search code examples
c#visual-studiodebuggingunhandled-exception

Visual studio breaks on exception that IS handled with unhandled exception dialog


My code calls a WCF service that is current not running. So we should expect the EndPointNotFoundException. The using statement tries to Close() the faulted connection which causes a CommunicationObjectFaultedException which is excepted. This exception is caught in a try catch block surrounding the using block:

class Program
{
    static void Main()
    {
        try
        {
            using (ChannelFactory<IDummyService> unexistingSvc = new ChannelFactory<IDummyService>(new NetNamedPipeBinding(), "net.pipe://localhost/UnexistingService-" + Guid.NewGuid().ToString()))
            {
                using (IClientChannel chan = (unexistingSvc.CreateChannel() as IClientChannel))
                {
                    (chan as IDummyService)?.Echo("Hello");
                }
            }
        }
        catch (EndpointNotFoundException ex)
        {
            Console.WriteLine("Expected");
        }
        catch (CommunicationObjectFaultedException ex)
        {
            Console.WriteLine("Expected: caused by closing channel that has thrown EndPointNotFoundException");
        }
    }
}

Note the service EndPoint uses a fresh Guid so it will never have a service listening.

IDummyService is:

[ServiceContract]
interface IDummyService
{
    [OperationContract]
    string Echo(string e);
}

This causes the Visual Studio debugger (Visual Studio Professional 2017 15.4.1) to break with an "Exception Unhandled" popup: Screen shot: Debugger breaking with Exception Unhandled popup The exception on which Visual Studio breaks is System.ServiceModel.CommunicationObjectFaultedException which is caught in the code.

Stepping to continue execution shows that catch(CommunicationObjectFaultedException ex) is reached. Using LinqPad to run the demo also shows that the exception is caught as expected.

I also tried explicitly (double) closing the channel instead of using the using-block:

class Program
{
    static void Main()
    {
        try
        {
            using (ChannelFactory<IDummyService> unexistingSvc = new ChannelFactory<IDummyService>(new NetNamedPipeBinding(), "net.pipe://localhost/UnexistingService-" + Guid.NewGuid().ToString()))
            {
                IDummyService chan = null;
                try
                {
                    chan = unexistingSvc.CreateChannel();
                    chan.Echo("Hello");
                }
                catch (EndpointNotFoundException ex)
                {
                    Console.WriteLine($"Expected: {ex.Message}");
                }
                finally
                {
                    try
                    {
                        (chan as IClientChannel)?.Close();
                    }
                    catch (CommunicationObjectFaultedException ex)
                    {
                        Console.WriteLine($"Caused by Close: {ex.Message}");
                    }
                }
            }
        }
        catch (EndpointNotFoundException ex)
        {
            Console.WriteLine("Expected");
        }
        catch (CommunicationObjectFaultedException ex)
        {
            Console.WriteLine("Expected: caused by closing channel that has thrown EndPointNotFoundException");
        }
    }
}

This still causes the debugger to break on the Close statement.

My Exception Settings has System.ServiceModel.CommunicationObjectFaultedException unchecked. (When it is checked Visual studio breaks as expected and with the "Exception Thrown" dialog instead of the "Exception Unhandled" dialog).

When I enable "Options"\"Debugging"\"General"\"Enable Just My Code" the debugger does not break. However, I have async methods where the exception should leave my code and I later catch the exception when awaiting the Task. For these methods I need "Enable Just My Code" unchecked; see Stop visual studio from breaking on exception in Tasks.

With "Using the New Exception Helper" disabled (as suggested by Jack Zhai-MSFT) Visual Studio still breaks and it shows Screenshot exception dialog when "Using the New Exception Helper" is disabled The dialog provides some additional information:

The exception is not caught before it crosses a managed/native boundary.

I suspect that the using block probably introduces this managed/native boundary.

What causes the debugger to break by mistake and how to make the debugger not break neither or handled CommunicationObjectFaultedExceptions nor on later handler async exceptions?


Solution

  • Close()-ing a Faulted IClientChannel causes a CommunicationObjectFaultedException:

    public void Close(TimeSpan timeout)
    {
        ...
        switch (originalState)
        {
            case CommunicationState.Created:
            case CommunicationState.Opening:
            case CommunicationState.Faulted:
                this.Abort();
                if (originalState == CommunicationState.Faulted)
                {
                    throw TraceUtility.ThrowHelperError(this.CreateFaultedException(), Guid.Empty, this);
                }
                break;
            ...
        }
        ...
    }
    

    -- (See CommunicationObject.Close(TimeSpan) line #299 in the .NET framework 4.7 reference source).

    The using-block is translated to try { ... } finally { Dispose(); } and Dispose() calls Close() when the block is left. The proxy returned by CreateChannel() is implemented via RealProxy (src) and RemotingServices.CreateTransparentProxy() these proxy combine managed and unmanaged code, which might cause the exception crossing the boundary.

    The combination of settings (in TOOLS->OPTIONS->Debugger->General):

    • ☑ Break when Exceptions cross AppDomain or managed/native boundaries
    • ☐ Enable Just My Code

    Causes Visual Studio to break showing either: the new non-modal exception popup "Exception Unhandled": Screenshot: Visual Studio: Exception Unhandled (New Exception Handler) or the modal dialog: Screenshot: Visual studio breaking on exception that crosses AppDomain or managed/native boundary

    The CommunicationObjectFaultedException starts in 'Not My Code'; it crosses an managed/unmanaged or AppDomain boundary while still being in 'Not My Code'; and finally enters 'My Code' where it is handled by the catch-block (but Visual Studio has already halted the excuting at this point).
    Since the exception starts in 'Not My Code' and remains there when crossing the boundary, selecting the option "Enable Just My Code" causes Visual studio to not break on the exception even when it crosses the AppDomain or managed/unmanaged boundary.
    Deselecting "break when exceptions cross AppDomain or managed/native boundaries" also causes Visual Studio to not break on the exception.

    This gives two solutions/workarounds