15

Ich schreibe eine Debugger-Erweiterung VSPackage, in dem ich eine Anweisung im debugged-Prozess ausführen möchte, wenn ein Haltepunkt erreicht wird. In meinem Dateisuffix habe ich dies:Kann keine Anweisung mit VS Debugger Interop ausführen

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 
     } 
    }    
} 

Beim Debuggen dieser Erweiterung in einer VS-Instanz suchen, wie dies Ich

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

Wenn der Haltepunkt in dem debuggt Prozess getroffen wird der Handler ein triviales Programm laden ist aufgerufen und das Ausgabefenster für die Erweiterung zeigt "3", so dass die Auswertung Ausdrücke funktioniert, aber es gelingt nie die Anweisung auszuführen. Es wird nichts mehr in das Ausgabefenster gedruckt. Es gibt keine Ausnahme oder Zeitüberschreitung, und ich kann den Prozess nicht weiter debuggen, der Debugger scheint abgestürzt zu sein.

Die Globals-Klasse hält nur die DTE und 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; } 
} 

Was ich tue, falsch, oder Missverständnis hier?

+0

Das Beste, was hier angenommen wird, ist, dass die Debugger-Engine noch nicht in einem Zustand ist, in dem sie Befehle empfangen kann. Standard-Trick ist es später mit einem Timer oder, sagen wir, ThreadPool.QueueUserWorkItem(), so dass es später passiert. –

+0

Verzögert es etwas, wird es manchmal einen Ausdruck ausführen. Ich habe das Gefühl, dass sie es durch erneutes Eingeben des Handlers töten könnte, als ob ExecuteStatement sofort OnEnterBreakMode auslöst (das könnte erklären, warum nichts passiert, wenn Sie es nicht etwas verzögern). –

Antwort

0

Ich habe viel mit Visual Studio-Debugging herumgebastelt, und die ultimative Ursache für das Einfrieren war immer mit der Thread-Handhabung verbunden: VS ermöglicht, dass jedes Codeteil während des Debuggens nur im Hauptthread ausgeführt wird. Jeder andere Thread ist deaktiviert, und falls Ihr Debug-Code von einem anderen Thread abhängt, wird er ebenfalls einfrieren.

Meine Vermutung: Sie initialisierten Ihre DTE in einem anderen Thread als das, was Sie debuggen.

Angenommenes Ergebnis: Die Delegate-Methode versucht, den Kontext des Initialisierungs-Threads zu laden, der sich vom debugged-Thread unterscheidet. Daher wird er zwangsläufig eingefroren.

Lösungsvorschlag: Verwenden Sie keine Delegate-Methode. Sie verweisen implizit auf den ursprünglichen Ausführungskontext. Stattdessen registrieren Sie eine reguläre Methode und initialisieren Sie Ihre DTE in diesem Kontext neu.

1

Dies ist eine alte Frage, aber es gibt so wenig auf Google über diese Probleme, ich dachte, ich würde etwas Hilfe anbieten. Einige wichtige Überlegungen:

  1. Verwendung GetExpresssion3 (TreatAsStatement: = True), wenn möglich, statt ExecuteStatement (ich konnte nicht ExecuteStatement einwandfreie Funktion).
  2. Der Thread, der den Delegaten aufruft (OnEnterBreakMode), ist derselbe Thread, der erneut ausgeführt werden muss, um den Ausdruck oder die Anweisung auszuführen. Rufen Sie daher Ihre GetExpression-Methode für einen neuen Thread auf (Task.Run ..)
  3. Sie müssen den Reason-Wert für OnEnterBreakMode überwachen und verwalten. Der ursprüngliche Grund ist UnwindFromException für die tatsächliche nicht behandelte Ausnahme. Dann wird erwartet, dass Sie eine Variable festlegen, z. B. tempStack = New System.Diagnostics.StackTrace (True). OnEnterBreakMode wird nach Ausführung dieser Anweisung erneut aufgerufen, diesmal jedoch mit Auswertung für den Grund. An dieser Stelle rufen Sie jetzt alle Ihrer GetExpressions an, um Ihre Ihrer Daten ohne zusätzliche OnEnterBreakMode-Aufrufe zu sammeln.

    Dim DTE2 Wie EnvDTE80.DTE2 = GetGlobalService (GetType (EnvDTE.DTE))

    Dim debugger5 als EnvDTE100.Debugger5 = DTE2.Debugger

Interessante Design Beobachtung: System.Diagnostics.StackTrace ist eine sehr seltsam gestaltet Klasse im Zusammenhang mit dem Rest des .NET-Framework, bis Sie an diesem Projekt arbeiten, wo Sie die Stacktrace durch diesen extrahieren sehr Technik und sehen Sie den Vorteil von seinem ansonsten seltsamen Design.