18

Desidero creare un'estensione per attivare rapidamente l'interruzione delle eccezioni CLR nel debugger.
Ho provato diversi approcci, nessuno dei quali è soddisfacente.Come impostare "Interrompi tutte le eccezioni", da un pacchetto

Ecco quello che ho già provato:

  1. ExceptionSettings.SetBreakWhenThrown (MSDN)
    Questo è estremamente lento (vedi this Connect issue). Ho provato gli approcci dalla domanda "Toggle “Break when an exception is thrown.” using macro or keyboard shortcut" e nessuno dei due sembra funzionare in modo affidabile: nella maggior parte dei casi viene impostata solo la casella di controllo di primo livello e in realtà non si interrompe sulle eccezioni durante il debug.

  2. chiamata DTE.ExecuteCommand("Debug.Exceptions") per visualizzare la finestra, e chiamare SetWindowsHookEx (MSDN) poco prima che a intercettarlo prima che venga visualizzato (in modo che non v'è alcun flash per l'utente). Questo sembra possibile perché sono stato in grado di intercettare il messaggio e ottenere HWND. Ma sembra hacky e la finestra non è così facile da manipolare correttamente (ha una strana combinazione di SysListView32 con caselle di controllo personalizzate e SysTreeView32). Quindi lo lascio come ultima soluzione.

  3. In qualche modo ottenere IDebugEngine2 (MSDN) per il codice gestito e chiamare IDebugEngine2.SetException (MSDN) all'inizio della sessione di debug. Questo sembra possibile, ma sto riscontrando problemi nell'ottenere un motore di debug. Ho provato l'approccio con IVsLoader descritto on MSDN forums, ma sono abbastanza sicuro che mi dà una nuova istanza non correlata alla sessione di debug.

    Ho anche posto la domanda qui: "Visual Studio: How to get IDebugEngine2 from VS Package (except IVsLoader)", ma non ho trovato una soluzione.

    Ho provato con IVsDebugger.AdviseDebugEventCallback (MSDN) e passando in attuazione della IDebugEventCallback2 (MSDN), ma sto ottenendo sempre null per pEngine (e nessun IDebugEngineCreateEvent2 o).

    io capisco IDebugSessionCreateEvent2 (non documentato?) E può ottenere IDebugSession2 da esso, ma la sua SetException chiamata mi dà sempre un HRESULT per argomento sbagliato, quindi mi potrebbe mancare qualcosa qui (chiamando SetException sul motore da IVsLoader dà OK, basta non funziona).

C'è qualche altro approccio che è meglio di quelli o ho perso qualcosa in quelli esistenti?


UPDATE/NOTA:
Se hai trovato questa domanda, perché si desidera una più veloce "si interrompe su tutti Eccezioni", ho fatto un'estensione gratuita è possibile ottenere da Visual Studio Gallery: Exception Breaker.

+2

Ti meriti più di un semplice +1 per lo sforzo che hai posto in questa domanda, quindi ho impiegato le ultime 3 ore a lavorare su una soluzione potenziale. :) Credo che al momento si possa applicare solo a VS2012, ma speriamo di poterlo fare anche nel 2010. –

+0

Apprezzo molto il tuo aiuto! Le mie capacità hanno attualmente raggiunto il loro limite con il debug a basso livello, quindi sono rimasto bloccato lì. Spero che questa estensione salverà le persone molto più tempo di quanto abbiamo speso per studiarlo. –

risposta

9

Le interfacce di automazione sono fuori questione. Nel tentativo di migliorare le prestazioni con il loro utilizzo, ho creato una cache dal gruppo di eccezioni all'oggetto ExceptionSettings e al nome dell'eccezione dell'oggetto ExceptionSetting.Ciò mi ha permesso di aggirare lo ExceptionSettings.Item per una rapida ricerca delle singole eccezioni per chiamare SetBreakWhenThrown, ma sfortunatamente l'implementazione interna di SetBreakWhenThrown include una chiamata per validare gli argomenti, che a sua volta innesca un processo di enumerazione interno che affligge l'intero approccio. La cache è circa 4 volte più veloce del codice che non utilizza una macro, ma stiamo ancora parlando di codice che bloccherà l'IDE per diversi minuti ...

NOTA: le istruzioni riportate di seguito sono state testate solo finora con Visual Studio 2012.

Passare attraverso SetBreakWhenThrown nella vista di disassemblaggio ha rivelato che la chiamata interna critica (dopo la convalida) è sdm::CDebugManager::SetException. Si scopre che il debugger di shell (servizio SVsShellDebugger che si trasmette a IVsDebugger) implementa IDebuggerInternal che fornisce l'accesso allo IDebugSession3 corrente. Questa proprietà era non nullo dopo l'apertura di una soluzione, ma prima ho iniziato il debug.

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

Nota: L'interfaccia IDebuggerInternal è definita in:

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

Utilizzando le informazioni restituite da EnumSetExceptions, ho creato una struttura che altera con successo le impostazioni per un'eccezione CLR! Chiamare IDebugSession3.SetException a abilitare l'arresto del debugger quando viene generata l'eccezione.

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); 

Per disattivare l'arresto debugger, utilizzare IDebugSession3.RemoveSetException invece.

+0

Questo è fantastico, grazie mille! Ho effettivamente trovato che 'AdviseDebugEventCallback' +' IDebugSessionCreateEvent2' funziona altrettanto bene, a patto che io usi la combinazione di flag invece di 'enum_EXCEPTION_STATE.EXCEPTION_STOP_ALL'. E la parte migliore è che Exception Dialog nota anche che il cambiamento, quindi non devo sostituirlo nella prima versione. –

+0

Trovato un problema. Quando chiamo 'SetException', questo influenza il contenuto di' Debug-> Exceptions' e generalmente funziona, ma non durante la sessione corrente. Ad esempio, se apro un'app console, la sessione (chiamiamola Session1) viene immediatamente creata per '* .vshost.exe' - quindi quando premo' Esegui', viene utilizzata la stessa sessione, quindi viene ricreata su 'Stop' (Session2). Quindi mentre 'Debug-> Exceptions' mostra le mie impostazioni, Session1 non si interrompe, ma Session2 lo fa anche senza impostarlo nuovamente. –

+0

Quindi 'SetException' sembra influenzare alcune opzioni interne persistenti tra le sessioni, ma la sessione corrente non ricarica immediatamente questa opzione. Presumo che 'Debug-> Exceptions' invochi una sorta di metodo di reinizializzazione/aggiornamento, ma non riesco a trovare nulla di adatto nelle API. –

Problemi correlati