18

Chcę zrobić rozszerzenie, aby szybko przełączać łamanie wyjątków CLR w debugerze.
Podjąłem próbę kilku podejść, z których żaden nie jest zadowalający.Jak ustawić "Przerwa na wszystkie wyjątki", z pakietu

Oto, co już próbowałem:

  1. ExceptionSettings.SetBreakWhenThrown (MSDN)
    ten jest bardzo powolny (patrz this Connect issue). Próbowałem podejść od pytania "Toggle “Break when an exception is thrown.” using macro or keyboard shortcut" i nie wydaje się działać niezawodnie: w większości przypadków ustawia się tylko pole wyboru najwyższego poziomu, a nie faktycznie włamać się do wyjątków podczas debugowania.

  2. połączeń DTE.ExecuteCommand("Debug.Exceptions") aby wyświetlić okno, i nazywają SetWindowsHookEx (MSDN) tuż przed tym, by przechwycić go, zanim pojawią się (tak, że nie ma lampy błyskowej dla użytkownika). Wydaje się to możliwe, ponieważ udało mi się przechwycić wiadomość i uzyskać HWND. Ale wygląda na to, że jest hacky i okno nie jest łatwe do manipulowania (ma dziwną kombinację SysListView32 ze specjalnymi polami wyboru i SysTreeView32). Pozostawiam to jako rozwiązanie ostatniej szansy.

  3. jakoś IDebugEngine2 (MSDN) dla kodu zarządzanego i nazywają IDebugEngine2.SetException (MSDN) na początku sesji debugowania. Wydaje się to możliwe, ale mam problemy z uzyskaniem mechanizmu debugowania. Próbowałem podejście z IVsLoader opisane on MSDN forums, ale jestem całkiem pewien, że daje mi nowe wystąpienie niezwiązane z sesji debugowania.

    Zadałem także pytanie tutaj: "Visual Studio: How to get IDebugEngine2 from VS Package (except IVsLoader)", ale nie otrzymałem rozwiązania.

    Próbowałem, używając IVsDebugger.AdviseDebugEventCallback (MSDN) i przechodząc w realizacji IDebugEventCallback2 (MSDN), ale zawsze jestem coraz null dla pEngine (i nie IDebugEngineCreateEvent2 albo).

    zrobić dostać IDebugSessionCreateEvent2 (nieudokumentowane?) I można dostać IDebugSession2 od niego, ale jego SetException wezwanie zawsze daje mi HRESULT o złym argumentem, więc może być brakuje czegoś tutaj (wywołanie SetException na silniku z IVsLoader daje OK, po prostu nie działa).

Czy istnieje inne podejście, które jest lepsze niż te lub czy coś przeoczyłem w istniejących?


UPDATE/UWAGA:
Jeśli znalazłeś na to pytanie, ponieważ chcesz szybciej „Przerwa na wszystkie wyjątki”, zrobiłem darmowe rozszerzenie można uzyskać z programu Visual Studio Gallery: Exception Breaker.

+2

Zasługujesz na więcej niż tylko +1 za wysiłek, jaki włożysz w to pytanie, więc poświęciłem 3 godziny na opracowanie potencjalnego rozwiązania. :) Wydaje mi się, że teraz może odnosić się tylko do VS2012, ale mam nadzieję, że uda nam się go uruchomić również w 2010 roku. –

+0

Naprawdę doceniam twoją pomoc! Moje umiejętności w tej chwili osiągnęły limit na niskim poziomie debugowania, więc utknąłem tam. Mam nadzieję, że to rozszerzenie zaoszczędzi ludziom znacznie więcej czasu, niż poświęciliśmy na badanie. –

Odpowiedz

9

Interfejsy automatyzacji są poza pytaniem. Aby zwiększyć wydajność przy ich użyciu, utworzyłem pamięć podręczną z grupy wyjątków na ExceptionSettings obiekt i nazwę wyjątku do obiektu ExceptionSetting.To pozwoliło mi ominąć ExceptionSettings.Item dla szybkiego wyszukiwania indywidualnych wyjątków dla wywołania SetBreakWhenThrown, ale niestety wewnętrzna implementacja SetBreakWhenThrown zawiera wywołanie do sprawdzenia poprawności argumentów, co z kolei wyzwala proces wewnętrznego wyliczania, który nęka to całe podejście. Pamięć podręczna jest około 4 razy szybsza niż kod nie używający makra, ale wciąż mówimy o kodzie, który zawiesza IDE na kilka minut ...

UWAGA: Poniższe instrukcje zostały tylko do tej pory przetestowane z użyciem Visual Studio 2012.

Przejście przez SetBreakWhenThrown w widoku demontażu ujawniło, że krytyczne wywołanie wewnętrzne (po sprawdzeniu poprawności) to sdm::CDebugManager::SetException. Okazuje się, że debuger powłoki (usługa SVsShellDebugger, którą obsadzono w jednostce IVsDebugger) implementuje IDebuggerInternal, który zapewnia dostęp do bieżącego IDebugSession3. Po uruchomieniu rozwiązania ta właściwość miała wartość inną niż null, ale przed rozpoczęciem debugowania.

IDebuggerInternal debugger = Package.GetGlobalService(typeof(SVsShellDebugger)) as IDebuggerInternal; 
IDebugSession3 session = debugger != null ? debugger.CurrentSession : null; 

Uwaga: Interfejs IDebuggerInternal jest zdefiniowana w:

Microsoft.VisualStudio.Debugger.Interop.Internal, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 

Korzystanie z informacji zwracanych przez EnumSetExceptions, stworzyłem strukturę, która z powodzeniem zmienia ustawienia wyjątku CLR! Zadzwoń pod IDebugSession3.SetException do enable zatrzymanie debuggera podczas zgłaszania wyjątku.

EXCEPTION_INFO[] exceptionInfo = 
{ 
    new EXCEPTION_INFO() 
    { 
     bstrExceptionName = typeof(NullReferenceException).FullName, 
     bstrProgramName = null, 
     dwCode = 0, 
     pProgram = null, 
     guidType = VSConstants.DebugEnginesGuids.ManagedOnly_guid, 
     dwState = enum_EXCEPTION_STATE.EXCEPTION_STOP_FIRST_CHANCE 
      | enum_EXCEPTION_STATE.EXCEPTION_STOP_SECOND_CHANCE 
      | enum_EXCEPTION_STATE.EXCEPTION_JUST_MY_CODE_SUPPORTED 
      | enum_EXCEPTION_STATE.EXCEPTION_STOP_USER_FIRST_CHANCE 
      | enum_EXCEPTION_STATE.EXCEPTION_STOP_USER_UNCAUGHT 
    } 
}; 
hr = session.SetException(exceptionInfo); 

Aby wyłączyć powstrzymania debugger, użyj IDebugSession3.RemoveSetException zamiast.

+0

To jest niesamowite, wielkie dzięki! Faktycznie stwierdziłem, że 'AdviseDebugEventCallback' +' IDebugSessionCreateEvent2' działa równie dobrze, o ile używam kombinacji twoich flag zamiast 'enum_EXCEPTION_STATE.EXCEPTION_STOP_ALL'. A najlepsze jest to, że okno dialogowe wyjątków również zauważa tę zmianę, więc nie muszę jej zastępować w pierwszej wersji. –

+0

Znaleziono problem. Kiedy wołam 'SetException', wpływa to na zawartość' Debug-> Exceptions' i na ogół działa, ale nie podczas bieżącej sesji. Na przykład, jeśli otworzę aplikację konsolową, sesja (nazwijmy ją Sesja1) jest natychmiast tworzona dla '* .vshost.exe' - wtedy gdy naciśniesz' Uruchom', zostanie użyta ta sama sesja, a następnie zostanie ponownie utworzona na 'Stop' (Session2). Tak więc, podczas gdy 'Debuguj-> Wyjątki' pokazuje moje ustawienie, Sesja 1 nie rozbija się na nim - ale Session2 działa nawet bez ustawiania go ponownie. –

+0

Tak więc 'SetException' wydaje się wpływać na niektóre wewnętrzne opcje utrzymywane między sesjami, ale bieżąca sesja nie przeładowuje natychmiast tej opcji. Zakładam, że 'Debug-> Exceptions' wywołuje jakąś metodę reinicjalizacji/aktualizacji, ale nie mogę znaleźć niczego odpowiedniego w API. –

Powiązane problemy