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:
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 await
ing 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
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 CommunicationObjectFaultedException
s nor on later handler async
exceptions?
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):
Causes Visual Studio to break showing either: the new non-modal exception popup "Exception Unhandled":
or the modal dialog:
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.
using
-block for the IClientChannel
and check IClientChannel.State
before Close()
-ing. As is done in What is the best workaround for the WCF client `using` block issue?