Не удается выполнить оператор с помощью VS Debugger Interop

Я пишу расширение отладки VSPackage, в котором я хочу выполнить инструкцию в отладочном процессе, когда ударяется точка останова. В моем добавочном коде у меня есть следующее:

void Initialize()
{
    // ...standard vspackage init code omitted...

    Globals.Init((DTE2)GetService(typeof(DTE)));              
    Globals.DebuggerEvents.OnEnterBreakMode += (dbgEventReason reason, ref dbgExecutionAction action) =>
    {
        try
        {
           var e1 = Globals.Application.Debugger.GetExpression("1+2");
           Debug.WriteLine(e1.Value);     // Prints "3"

           Globals.Application.Debugger.ExecuteStatement("x = 1+2", 1000);
           Debug.WriteLine("OK");         // Never prints this                          
        } 
        catch (Exception ex)
        {
           Debug.WriteLine("Error: "+ex); // Nor this
        }
    }             
}

При отладке этого расширения в экземпляре VS я загружаю тривиальную программу, похожую на это

static void Main()
{
   int x = 5;
   Console.WriteLine("X is "+x); // Breakpoint on this line
}

Когда точка останова попадает в отлаженный процесс, вызывающий обработчик вызывается, а окно вывода для расширения показывает "3", поэтому оценка выражений работает, но никогда не удается выполнить оператор. В окне вывода ничего больше не печатается. Исключение или тайм-аут не происходит, и я не могу продолжить отладку процесса, кажется, что отладчик разбился.

Класс globals содержит только DTE и DebuggerEvents

public static class Globals
{
   public static void Init(DTE2 dte)
   {
      Application = dte;
      DebuggerEvents = dte.Events.DebuggerEvents;    
   }

   public static DTE2 Application { get; private set; }
   public static DebuggerEvents DebuggerEvents { get; private set; }
}

Что я делаю неправильно или неправильно понимаю здесь?

Ответы

Ответ 1

Это старый вопрос, но в Google об этих проблемах так мало, я думал, что помогу. Некоторые важные соображения:

  • Используйте GetExpresssion3 (TreatAsStatement: = True), если это возможно, вместо ExecuteStatement (я не смог нормально работать с ExecuteStatement).
  • Нить, вызывающая ваш делегат (OnEnterBreakMode), - это тот же самый поток, который понадобится, чтобы запустить снова, чтобы выполнить ваше выражение или инструкцию. Поэтому вызовите метод GetExpression в новом потоке (Task.Run..)
  • Вам нужно будет контролировать и управлять значением Reason для OnEnterBreakMode. Первоначальный разум - это UnwindFromException для фактического необработанного исключения. Затем ожидается, что вы установите переменную, например tempStack = New System.Diagnostics.StackTrace(True). OnEnterBreakMode будет вызываться снова после выполнения этого оператора, но на этот раз с оценкой для причины. На этом этапе вы теперь вызываете все своих GetExpressions для сбора всех ваших данных без дополнительных вызовов OnEnterBreakMode.

    Dim dte2 As EnvDTE80.DTE2 = GetGlobalService (GetType (EnvDTE.DTE))

    Dim debugger5 as EnvDTE100.Debugger5 = Dte2.Debugger

Интересное наблюдение за дизайном: System.Diagnostics.StackTrace - очень странно разработанный класс в контексте остальной части платформы .NET, пока вам не придется работать над этим проектом, где вы извлекаете StackTrace с помощью этой самой техники и смотрите выгода от его иначе нечетной конструкции.

Ответ 2

Я много разбирался с отладкой Visual Studio, и конечная причина замораживания всегда была связана с обработкой потоков: VS позволяет запускать любую часть кода во время отладки только в основном потоке. Каждый другой поток отключается, и если ваш код отладки зависит от другого потока, он также замерзнет.

Мое предположение: вы инициализировали свой DTE в другом потоке, чем то, что вы отлаживаете.

Предполагаемый результат: метод Delegate пытается загрузить контекст инициализирующего потока, который отличается от отлаженного потока, и, следовательно, он будет заморожен.

Предлагаемое решение: Не используйте метод делегата. Они неявно ссылаются на исходный контекст исполнения. Вместо этого зарегистрируйте регулярный метод и повторите инициализацию своего DTE в этом контексте.