在Visual Studio中以编程方式应用/停用断点

21
无论有哪些其他选项可以达到相同的结果(如手动添加断点),是否可能在Visual Studio项目的源代码中编程方式地添加断点?
例如:
try
{
    FunctionThatThrowsErrors(obj InscrutableParameters);
}
catch(Exception ex)
{
    Log.LogTheError(ex);
    AddBreakPointToCallingFunction();
}

这样一来,当你下次在调试时运行代码时,它将自动设置断点,在上一次运行中导致问题的所有点上。

我并不是说这是一种特别有用的调试方式。我只是想知道是否有这个能力。


这是我经常用C++实现的方式:if (IsDebuggerPresent()) DebugBreak();,我把它放在了我的几个异常类的构造函数中。 - Mooing Duck
5个回答

50
您可以调用 System.Diagnostics.Debugger.Break()
您还可以告诉Visual Studio在所有异常上中断,即使是已处理的异常,在菜单栏上选择Debug->Exceptions...,并勾选当前仅勾选了“未经用户处理”的所有地方的Thrown

首先,(+1) 真是太棒了,我不知道它的存在。但是,文档指出:“如果没有调试器附加,用户会被询问是否要附加调试器。”这意味着如果在没有调试器的情况下运行,该方法将中断当前执行流程。在我的情况下,我希望它为下一次设置断点。此外,问题说明我想在调用函数中设置断点,因为(大概)调用函数具有重要的上下文信息未传递。 - DevinB
编译后无法更改代码。这意味着你想要做的事情涉及到在始终被检查的配置标志后面提高断点,并在你的异常中更改配置。这可能需要大量的配置。在这种情况下,最好的选择是在抛出时中断,就像我的编辑所示的那样。 - Joel Coehoorn
7
在调用中断之前,您可以检查 Debugger.IsAttached。 - Joel Coehoorn
我之前也用过那个工具,它非常有用。但是,那假设我已经在调试器中运行了。我希望找到一个解决方案,在第一次运行时不会干扰(不显眼?)(即生产环境),但可以在调试模式下运行,并且可以突出显示(而无需手动设置断点)上次运行的问题点。假设我可以获得相同的输入(例如,我正在运行报告或解析输入数据)。 - DevinB
你能做的最好的事情就是给你的异常命名/标识符并记录下来,然后在以后的调试模式中,在每个点检查并查看当前的名称/标识符是否已被记录。 - Joel Coehoorn
1
几年有点晚了,但你可以将该语句封装在编译器指令 #if (DEBUG) Debugger.Break(); #endif 中。 - Raheel Khan

41

你的启发让我研究了一下这个问题,感谢让我彻夜未眠。 :) 下面是一种解决方法。

Visual Studio具有非常出色的断点支持。其中一个更酷的功能是,您可以告诉它在断点命中时运行Visual Studio宏。这些宏可以完全访问开发环境,即它们可以执行您可以手动在键盘上执行的任何操作,包括设置其他断点。

此解决方案的步骤如下:1)在程序中放置顶层try/catch以捕获所有异常,2)在catch块中放置断点以运行您的宏,3)使宏查看异常以确定其来源并在那里设置一个断点。当您在调试器中运行它并发生异常时,您将在有问题的代码行处有一个新的断点。

以这个示例程序为例:

using System;

namespace ExceptionCallstack
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                func1();
            }
            catch (Exception e)
            {
                Console.WriteLine("Oops");
                Console.ReadKey();
            }
        }

        static void func1()
        {
            func2();
        }

        static void func2()
        {
            func3();
        }

        static void func3()
        {
            throw new Exception("Boom!");
        }
    }
}

目标是在调试器中运行并获取错误时,在func3的throw上编程设置断点。为此,首先创建一个新的Visual Studio宏(我称其为SetBreakpointOnException)。将以下内容粘贴到新模块MyDebuggerMacros或其他模块中:

Imports System
Imports EnvDTE
Imports EnvDTE80
Imports EnvDTE90
Imports System.Diagnostics
Imports System.Text.RegularExpressions

Public Module DebuggerMacros

    Sub SetBreakpointOnException()

        Dim output As String = ""

        Dim stackTrace As String = DTE.Debugger.GetExpression("e.StackTrace").Value
        stackTrace = stackTrace.Trim(New Char() {""""c})
        Dim stackFrames As String() = Regex.Split(stackTrace, "\\r\\n")

        Dim r As New Regex("^\s+at .* in (?<file>.+):line (?<line>\d+)$", RegexOptions.Multiline)
        Dim match As Match = r.Match(stackFrames(0))
        Dim file As String = match.Groups("file").Value
        Dim line As Integer = Integer.Parse(match.Groups("line").Value)

        DTE.Debugger.Breakpoints.Add("", file, line)

    End Sub

End Module

一旦这个宏放置好了,回到catch块并使用F9设置一个断点。然后右键单击红色断点圆圈并选择“命中时...”。在结果对话框的底部,有一个选项告诉它运行宏-下拉列表并选择您的宏。现在,当您的应用程序抛出未处理的异常时,应该会得到新的断点。

关于此事项的说明和注意事项:

  • 我不是正则表达式专家,我相信其他人可以编写更好的东西。
  • 这不处理嵌套异常(InnerException属性)-如果你想要的话,你可以自己尝试。:)检查GetExpression("e.InnerException")并递归,也许可以解决。
  • 它对异常的StackTrace字符串进行文本解析,而不是更复杂的对象图分析(深入Exception.TargetSite并使用反射)。通常的警告适用于这种方法的脆弱性。
  • 由于某种原因,它似乎将断点放入了某个“备用空间”。一旦初始调试会话结束,您将看不到代码中的新断点。但是,如果您再次在调试器中运行程序,则存在该断点,并且诸如“禁用所有断点”之类的功能会影响它。如果有人愿意找到清理它的方法,了解发生了什么将是不错的。也许在.suo文件中挖掘?

希望这可以帮助到你!


哇!!!(我本来想只打“哇!!!”但是它不允许少于15个字符 :) ) - Rashmi Pandit
天啊,如果有办法让我给你加10分的话,我一定会这么做。 - DevinB
明天我得回来给这个答案点赞,于是我需要在手机上设置一个提醒。 :-) - Henric
对于那些来晚了的人,我想提醒一下:显然在Visual Studio中不再有内置宏了,尽管有一个扩展程序https://marketplace.visualstudio.com/items?itemName=VisualStudioPlatformTeam.MacrosforVisualStudio - 但我不确定是否可以像这个答案中那样编写Visual Basic脚本;如果可能的话,那就太好了。 - sdaau

4

虽然这并不直接回答你的问题,但你可以使用Debug.Assert设置条件使得调试器在满足条件时中断。因此,你可以向函数添加断言,以在条件不符合预期时中断,而不是说“下次运行引发异常的函数时中断”,毕竟并不保证该函数这次一定会抛出异常,即使它上次抛出了异常。 :)


2

我不认为你真的可以“添加断点”,但是你可以通过调用System.Diagnostics.Debugger.Break()指示调试器暂停执行,以便检查出错的原因。


0
此外,Visual Basic 有一个关键字叫做 Stop,它可以作为断点来中断程序的执行。

1
我相信你可能误解了问题。我希望执行程序修改pdb文件,而不是源代码。这样,我可以正常运行它第一次,并查看它的处理方式,然后我可以附加调试器,它将在所有上次失败的地方自动设置断点。你的停止解决方案将始终停止执行。 - DevinB

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