15

Piszę rozszerzenie debugger VSPackage, w którym chcę wykonać instrukcję w debugowane procesu, gdy punkt przerwania jest trafiony. W moim numerem kierunkowym mam to:Nie można wykonać instrukcji z VS Debugger Interop

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

Podczas debugowania tego rozszerzenia w instancji VS załadować trywialny programu wygląda tak

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

Gdy punkt przerwania jest trafiony w debugowanego procesu obsługi jest wywoływane, a okno wyjściowe dla rozszerzenia pokazuje "3", więc sprawdzanie wyrażeń działa, ale nigdy nie powiedzie się wykonanie instrukcji. Nic więcej nie jest drukowane w oknie wyjściowym. Nie ma wyjątku ani limitu czasu i nie mogę kontynuować debugowania procesu, debugger prawdopodobnie się zawiesił.

Klasa globalne tylko trzyma DTE i 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; } 
} 

Co robię źle, czy nieporozumienie?

+0

Najlepiej założyć, że silnik debuggera nie znajduje się jeszcze w stanie, w którym może zacząć wykonywać polecenia. Standardowa sztuczka polega na tym, aby zrobić to później za pomocą timera lub, powiedzmy, ThreadPool.QueueUserWorkItem(), aby stało się to później. –

+0

Opóźnianie pomaga w pewnym stopniu, że czasami wykona wyrażenie. Mam wrażenie, że mogę go zabić, ponownie wchodząc do programu obsługi, tak jakby ExecuteStatement natychmiast podnosił OnEnterBreakMode (co mogłoby wyjaśnić, dlaczego nic się nie dzieje, chyba że trochę go opóźnisz). –

Odpowiedz

0

Dużo majstrowałem przy debugowaniu Visual Studio, a ostateczna przyczyna zamrożenia była zawsze związana z obsługą wątków: VS pozwala na uruchamianie dowolnego fragmentu kodu podczas debugowania tylko w głównym wątku. Każdy inny wątek jest wyłączony i na wypadek, gdyby kod debugowania zależał od innego wątku, również zostanie zamrożony.

Zgaduję: Zainicjowałeś swój DTE w innym wątku niż debugowanie.

Założony wynik: Metoda delegata próbuje wczytać kontekst wątku inicjującego, który różni się od debugowanego wątku, a tym samym zostanie zablokowany.

Proponowane rozwiązanie: Nie należy stosować metody delegowania. Odwołują się pośrednio do pierwotnego kontekstu wykonania. Zamiast tego zarejestruj regularną metodę i ponownie zainicjuj swój DTE w tym kontekście.

1

To jest stare pytanie, ale w Google jest tak mało informacji na temat tych problemów. Pomyślałem, że zaoferuję pomoc. Niektóre ważne względy:

  1. Zastosowanie GetExpresssion3 (TreatAsStatement: = True), jeśli to możliwe, zamiast ExecuteStatement (nie mogłem dostać ExecuteStatement działa poprawnie).
  2. Wątek wywołujący delegata (OnEnterBreakMode) jest tym samym wątkiem, który będzie potrzebny do ponownego uruchomienia w celu wykonania wyrażenia lub wyciągu. Dlatego wywołaj swoją metodę GetExpression na nowym wątku (Task.Run ..)
  3. Będziesz musiał monitorować i zarządzać wartością Reason dla OnEnterBreakMode. Początkowym powodem jest UnwindFromException dla faktycznego nieobsługiwanego wyjątku. Następnie oczekuje się, że ustawiasz zmienną, taką jak tempStack = New System.Diagnostics.StackTrace (True). OnEnterBreakMode zostanie wywołany ponownie po wykonaniu tej instrukcji, ale tym razem z Evaluation for the Reason. W tym momencie zadzwoń teraz pod numer pod numer swojego GetExpressions, aby zebrać dane bez dodatkowych wywołań OnEnterBreakMode.

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

    Dim debugger5 jak EnvDTE100.Debugger5 = Dte2.Debugger

Ciekawe spostrzeżenie design: System.Diagnostics.StackTrace to bardzo dziwnie zaprojektowane klasy w kontekście reszty NET aż trzeba pracować nad tym projektem, gdzie jesteś wydobywania StackTrace przez to bardzo techniki i zobacz korzyści wynikające z jego dziwnego projektowania.

Powiązane problemy