Вы вдохновили меня возиться с этим - спасибо, что не давали мне спать всю ночь. :) Вот один из способов сделать это.
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!");
}
}
}
Цель состоит в том, чтобы программно установить точку останова на эту throw
в func3, когда вы запустите ее в отладчике и получите ошибку. Для этого сначала создайте новый макрос 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?
Надеюсь, это поможет!