Visual Studio在处理已处理的异常时中断,并显示未处理异常对话框。

5

我的代码调用了一个当前未运行的WCF服务。因此,我们应该期望出现EndPointNotFoundException异常。使用语句尝试Close()故障连接,导致了一个CommunicationObjectFaultedException异常。这个异常被包含在一个try catch块中,围绕着使用块:

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");
        }
    }
}

请注意,服务端点使用新的Guid,因此它永远不会有服务在监听。

IDummyService是:

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

这会导致Visual Studio调试器(Visual Studio Professional 2017 15.4.1)出现“异常未处理”弹窗: 屏幕截图:调试器出现异常未处理弹窗 在代码中已经捕获了Visual Studio中断的异常:System.ServiceModel.CommunicationObjectFaultedException
继续执行显示已经到达catch(CommunicationObjectFaultedException ex)。使用LinqPad运行演示也表明异常按预期被捕获。
我还尝试了显式(双重)关闭通道,而不是使用using块:
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");
        }
    }
}

即使加上 Close 语句,调试器仍会停在该语句上。

我的异常设置中,System.ServiceModel.CommunicationObjectFaultedException 没有被勾选。(当勾选时,Visual Studio 会弹出“异常抛出”对话框而非“未处理异常”对话框)。

当我启用“选项”\“调试”\“常规”\“启用我的代码”时,调试器不会停止。但是,我有一些带有 async 方法的代码,其中异常应该离开我的代码,并在 await Task 时才捕获异常。对于这些方法,我需要取消勾选“启用我的代码”,请参见Stop visual studio from breaking on exception in Tasks

即使已禁用“使用新的异常助手”(如Jack Zhai-MSFT所建议),Visual Studio 仍会停止并显示以下内容: Screenshot exception dialog when "Using the New Exception Helper" is disabled 此对话框提供了一些额外的信息:

异常在跨越 托管/本机 边界之前未被捕获。

我怀疑使用块可能会引入此托管/本机边界。

是什么导致了调试器的误报,如何使调试器既不在处理 CommunicationObjectFaultedException 时停止,也不在后续处理 async 异常时停止?


1
{btsdaf} - Jack Zhai
{btsdaf} - Kasper van den Berg
{btsdaf} - Kasper van den Berg
{btsdaf} - Jack Zhai
2个回答

2
新的异常功能在VS2017中推出,我们可以在TOOLS->OPTION->Debugging->General下禁用调试选项"Use the New Exception Helper",这将提供旧的异常消息,您可以访问它。
旧的异常消息显示异常跨越了托管/本机边界: Screenshot exception dialog when "Using the New Exception Helper" is disabled 在TOOLS->OPTION->Debugging->General下勾选"Break when Exceptions cross AppDomain or managed/native boundaries"。禁用"Break when Exceptions cross AppDomain or managed/native boundaries"以避免Visual Studio在OP的情况下中断(但请注意,这也会禁用在其他异常跨越AppDomain或托管/本机边界的情况下中断)。

1

Close()关闭一个FaultedIClientChannel会导致一个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;
        ...
    }
    ...
}

-- (请参见CommunicationObject.Close(TimeSpan)第299行.NET Framework 4.7参考源代码中)。

using 块被翻译为 try { ... } finally { Dispose(); },并且当块结束时,Dispose() 调用 Close()CreateChannel() 返回的代理通过 RealProxysrc)和 RemotingServices.CreateTransparentProxy() 实现,这些代理结合了托管和非托管代码,可能会导致异常跨越边界。

设置组合(在工具->选项->调试器->常规中):

  • ☑ 当异常跨越应用程序域或托管/本机边界时中断
  • ☐ 启用我的代码

导致Visual Studio中断,显示以下内容之一:新的非模态异常弹出窗口“未处理的异常”: 屏幕截图:Visual Studio:未处理的异常(新异常处理程序) 或模态对话框: 屏幕截图:Visual studio在跨越应用程序域或托管/本机边界的异常上中断

CommunicationObjectFaultedException 的出现始于“非本代码”,在跨越托管/非托管或应用程序域边界时仍处于“非本代码”状态,在最后进入“我的代码”并由 catch 块处理(但此时 Visual Studio 已经停止执行)。
由于异常是从“非本代码”开始并在跨越边界时仍然保持在那里,因此选择“启用仅限我的代码”选项会导致 Visual Studio 即使在跨越应用程序域或托管/非托管边界时也不会中断异常。
取消选择“跨应用程序域或托管/非托管边界时中断异常”也会导致 Visual Studio 不会中断异常。

这提供了两个解决方案/变通方法


我也遇到了EndPointNotFoundException的问题。这个设置解决了我的问题!谢谢。 - z00mable

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接