【问题标题】:Visual studio breaks on exception that IS handled with unhandled exception dialogVisual Studio 在使用未处理的异常对话框处理的异常时中断
【发布时间】:2018-04-01 17:07:22
【问题描述】:

我的代码调用了当前未运行的 WCF 服务。所以我们应该期待EndPointNotFoundException。 using 语句尝试Close() 导致出现异常的CommunicationObjectFaultedException 的连接。此异常在 using 块周围的 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");
        }
    }
}

请注意,服务 EndPoint 使用新的 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-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");
        }
    }
}

这仍然会导致调试器在 Close 语句上中断。

我的异常设置未选中 System.ServiceModel.CommunicationObjectFaultedException。 (当它被选中时,Visual Studio 会按预期中断并使用“抛出异常”对话框而不是“异常未处理”对话框)。

当我启用“选项”\“调试”\“常规”\“启用我的代码”时,调试器不会中断。但是,我有 async 方法,异常应该离开我的代码,后来我在 awaiting Task 时捕获了异常。对于这些方法,我需要取消选中“仅启用我的代码”;见Stop visual studio from breaking on exception in Tasks

禁用“使用新的异常帮助程序”(如 Jack Zhai-MSFT 所建议的那样)Visual Studio 仍然中断并显示 该对话框提供了一些附加信息:

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

我怀疑 using 块可能引入了这个托管/本机边界。

是什么导致调试器错误中断以及如何使调试器既不中断也不处理CommunicationObjectFaultedExceptions 或以后的处理程序async 异常?

【问题讨论】:

  • 如果在VS2017中禁用TOOLS->OPTION->Debugging->General下的调试选项“Use the New Exception Helper”,结果如何?
  • @JackZhai-MSFT 我在禁用“使用新的异常帮助程序”的情况下进行了测试,并将结果包含在问题中。旧的异常对话为尝试解决问题提供了一些新途径。
  • @JackZhai-MSFT 谢谢,您的建议帮助我通过正确的设置解决了问题。现在发布答案。
  • 很高兴知道它对您有帮助,我将其添加为有用的答案,当然,如果您有其他有用的信息来补充它,您可以编辑该答案。祝你有美好的一天:)

标签: c# visual-studio debugging unhandled-exception


【解决方案1】:

VS2017新增异常功能,我们可以在TOOLS->OPTION->Debugging->General下禁用调试选项“Use the New Exception Helper”,它可以为你提供旧的异常消息,你可以访问它。

旧的异常消息显示异常跨越了托管/本地边界:

在“工具”->“选项”->“调试”->“常规”下选中“异常跨越 AppDomain 或托管/本机边界时中断”。禁用“当异常跨越 AppDomain 或托管/本机边界时中断”以避免 Visual Studio 在 OP 的情况下中断(但要小心,因为这也会在异常跨越 AppDomain 或托管/本机边界的其他情况下禁用中断)。

【讨论】:

    【解决方案2】:

    Close()-ing a Faulted IClientChannel 导致 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;
            ...
        }
        ...
    }
    

    --(参见.NET framework 4.7 reference source中的CommunicationObject.Close(TimeSpan) line #299)。

    using-block 被转换为try { ... } finally { Dispose(); },当块离开时Dispose() 调用Close()CreateChannel() 返回的代理是通过RealProxy (src) 和RemotingServices.CreateTransparentProxy() 实现的,这些代理结合了托管和非托管代码,可能会导致异常越界。

    设置组合(在TOOLS->OPTIONS->Debugger->General中):

    • ☑ 当异常跨越 AppDomain 或托管/本地边界时中断
    • ☐ 仅启用我的代码

    导致 Visual Studio 中断显示:新的非模态异常弹出窗口“异常未处理”: 或模态对话框:

    CommunicationObjectFaultedException 以“不是我的代码”开头;它跨越托管/非托管或 AppDomain 边界,同时仍处于“不是我的代码”中;最后进入由catch-block 处理的“我的代码”(但此时Visual Studio 已经停止执行)。
    由于异常从“不是我的代码”开始并在跨越边界时仍然存在,因此选择“仅启用我的代码”选项会导致 Visual Studio 不会中断异常,即使它跨越 AppDomain 或托管/非托管边界。
    取消选择“异常跨越 AppDomain 或托管/本机边界时中断”也会导致 Visual Studio 不会中断异常。

    这提供了两种解决方案/解决方法

    【讨论】:

    • EndPointNotFoundException 也有同样的问题。此设置为我修复了它!谢谢。
    猜你喜欢
    • 2010-09-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-26
    • 2017-01-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多