在 Visual Studio 中使用调试器管理异常

异常指示在执行程序时发生的错误状态。 可以告诉调试器要中断哪些异常或异常集,以及希望调试器中断(即在调试器中暂停)的哪个时间点。 调试器中断时,它会显示引发异常的位置。 还可以添加或删除异常。 在 Visual Studio 中打开解决方案后,使用 “调试 > Windows > 异常设置” 打开 “异常设置” 窗口。

提供响应最重要的异常的处理程序。 如果需要了解如何为异常添加处理程序,请参阅 通过编写更好的 C# 代码来修复 bug。 此外,了解如何将调试器配置为在某些异常出现时始终中断执行。

发生异常时,调试器会将异常消息写入 “输出 ”窗口。 在以下情况下,可能会中断执行:

  • 抛出未处理的异常。
  • 调试器配置为在调用任何处理程序之前中断执行。
  • 已设置“仅我的代码”,并且调试器配置为在任何未在用户代码中处理的异常时中断。

注释

ASP.NET 有一个顶级异常处理程序,该处理程序在浏览器中显示错误页。 除非打开 “仅我的代码 ”,否则它不会中断执行。 有关示例,请参阅下文告知调试器在用户未处理异常时继续执行

注释

在 Visual Basic 应用程序中,调试器将所有错误作为异常进行管理,即使使用 On Error 样式错误处理程序也是如此。

指示调试器在引发异常时中断

调试器可以在引发异常时中断执行,因此可以在调用处理程序之前检查异常。

“异常设置” 窗口中(调试 > Windows > 异常设置),展开异常类别的节点,例如 公共语言运行时异常。 然后选中该类别中特定异常的复选框,例如 System.AccessViolationException。 还可以选择整个类别的例外。

“异常设置”复选框的屏幕截图。

小窍门

可以使用“异常设置”工具栏中的“搜索”窗口查找特定异常,或使用搜索来筛选特定命名空间(如 System.IO)。

如果在 “异常设置” 窗口中选择异常,无论是否处理异常,调试器执行都将中断引发异常的位置。 现在,异常称为第一次机会异常。 例如,下面是几个方案:

  • 在以下 C# 控制台应用程序中,Main 方法在 try/catch 块内引发 AccessViolationException

    static void Main(string[] args)
    {
        try
        {
            throw new AccessViolationException();
            Console.WriteLine("here");
        }
        catch (Exception e)
        {
            Console.WriteLine("caught exception");
        }
        Console.WriteLine("goodbye");
    }
    

    如果在异常设置中选中了AccessViolationException,当您在调试器中运行此代码时,执行将在throw行中断。 然后,可以继续执行。 控制台应显示这两行:

    caught exception
    goodbye
    

    但它不显示线条 here

  • C# 控制台应用程序引用具有两种方法的类的类库。 一种方法会引发异常并处理该异常,而第二种方法引发相同的异常,但不处理该异常。

    public class Class1
    {
        public void ThrowHandledException()
        {
            try
            {
                throw new AccessViolationException();
            }
            catch (AccessViolationException ave)
            {
                Console.WriteLine("caught exception" + ave.Message);
            }
        }
    
        public void ThrowUnhandledException()
        {
            throw new AccessViolationException();
        }
    }
    

    下面是控制台应用程序的 Main() 方法:

    static void Main(string[] args)
    {
        Class1 class1 = new Class1();
        class1.ThrowHandledException();
        class1.ThrowUnhandledException();
    }
    

    如果在异常设置中选中AccessViolationException,在调试器中运行此代码时,执行将在throwThrowUnhandledException()中的行上中断。

若要将异常设置还原为默认值,请选择“ 将列表还原到默认设置 ”按钮:

异常设置中还原默认值的屏幕截图。

告知调试器继续处理用户未处理的异常

如果要使用 “仅我的代码”调试 .NET 或 JavaScript 代码,则可以告诉调试器防止在用户代码中未处理但在其他位置处理的异常中断。

  1. “异常设置” 窗口中,右键点击列标签打开快捷菜单,然后选择 “显示列 > 其他操作”。 (如果已关闭 “仅我的代码”,则看不到此命令。)将显示名为 “其他操作” 的第三列。

    “其他操作列”的屏幕截图。

    对于在此列中显示在用户代码中未处理时继续的异常,如果该异常未在用户代码中处理,但在外部处理,调试器将继续。

  2. 若要更改特定异常的此设置,请选择该异常,右键单击以显示快捷菜单,然后选择“ 在用户代码中取消处理时继续”。 还可以更改整个异常类别的设置,例如整个公共语言运行时异常)。

    在用户代码设置中未处理时继续的屏幕截图。

例如,ASP.NET Web 应用程序通过将异常转换为 HTTP 500 状态代码(ASP.NET Web API 中的异常处理)来处理异常,但这可能无法帮助您确定异常的来源。 在下面的示例中,用户代码调用 String.Format(),抛出 FormatException。 执行中断的顺序如下:

在用户未处理的异常 上中断

添加和删除异常

可以添加和删除异常。 若要从类别中删除异常类型,请选择该异常,并从“例外设置”工具栏上的“列表”按钮(减号)中选择“删除所选异常”。 或者,可以右键单击该异常,然后从快捷菜单中选择“ 删除 ”。 删除异常的效果与将异常设为不受检查的效果相同,即调试器在异常被引发时不会中断。

若要添加异常,请:

  1. “异常设置” 窗口中,选择一个异常类别(例如 公共语言运行时)。

  2. 选择向所选类别添加异常按钮(加号)。

    向所选类别按钮添加异常的屏幕截图。

  3. 键入异常的名称(例如 System.UriTemplateMatchException)。

    类型异常名称的屏幕截图。

    异常将添加到列表中(按字母顺序排列),并自动选中。

若要向 GPU 内存访问异常、JavaScript 运行时异常或 Win32 异常类别添加异常,请包括错误代码和说明。

小窍门

检查拼写! “异常设置”窗口不会检查是否存在已添加的异常。 因此,如果键入 Sytem.UriTemplateMatchException,你将获得该异常的条目(而不是 System.UriTemplateMatchException)。

异常设置保存在解决方案的 .suo 文件中,因此它们适用于特定解决方案。 不能跨解决方案重复使用特定的异常设置。 现在仅保留被添加的异常,已删除的异常不被保留。 可以添加异常,关闭并重新打开解决方案,并且该异常仍然存在。 但是,如果删除异常并关闭/重新打开解决方案,该异常将重新出现。

异常设置” 窗口支持 C# 中的泛型异常类型,但在 Visual Basic 中不支持。 若要中断类似异常 MyNamespace.GenericException<T>,必须将异常添加为 MyNamespace.GenericException'1。 也就是说,如果已创建类似以下代码的异常:

public class GenericException<T> : Exception
{
    public GenericException() : base("This is a generic exception.")
    {
    }
}

可以使用前面的过程将异常添加到 异常设置

添加泛型异常的屏幕截图。

向异常添加条件

使用 “异常设置” 窗口设置异常的条件。 当前支持的条件包括用于异常包含或排除的模块名称。 通过将模块名称设置为条件,可以选择仅在某些代码模块上中断异常。 还可以选择避免中断特定模块的执行。

注释

从 Visual Studio 2017 开始,支持将条件添加到异常。

若要设置条件异常,请执行以下步骤:

  1. 在“异常设置”窗口中选择 “编辑条件 ”按钮,或右键单击该异常,然后选择 “编辑条件”。

    异常条件的屏幕截图。

  2. 若要向异常添加额外的必需条件,请选择每个新条件 的“添加条件 ”。 将显示其他条件行。

    异常的额外条件的屏幕截图。

  3. 对于每个条件行,键入模块的名称,并将比较运算符列表更改为 EqualsNot Equals。 可以在名称中指定通配符(\*)以指定多个模块。

  4. 如果需要删除条件,请选择条件行末尾的 X