Come ottenere l'intera catena di eccezioni nel gestore eventi Application.ThreadException?
Domanda
Stavo lavorando per sistemare la gestione delle eccezioni in un'app .NET 2.0 e mi sono imbattuto in uno strano problema con Application.ThreadException.
Quello che voglio è essere in grado di catturare tutte le eccezioni dagli eventi dietro gli elementi della GUI (ad es.pulsante_clic, ecc.).Voglio quindi filtrare queste eccezioni in base alla "fatalità", ad es.con alcuni tipi di eccezioni l'applicazione dovrebbe continuare a funzionare e con altri dovrebbe uscire.
In un'altra app .NET 2.0 ho appreso che, per impostazione predefinita, solo in modalità debug le eccezioni lasciano effettivamente una chiamata Application.Run o Application.DoEvents.Nella modalità di rilascio ciò non accade e le eccezioni devono essere "catturate" utilizzando l'evento Application.ThreadException.
Ora, però, l'ho notato l'oggetto eccezione passato nel ThreadExceptionEventArgs dell'evento Application.ThreadException è sempre l'eccezione più interna nella catena di eccezioni.Per scopi di registrazione/debug/progettazione, però, voglio davvero l'intera catena di eccezioni.Non è facile determinare quale sistema esterno ha fallito, ad esempio quando riesci a gestire una SocketException:quando è avvolto come ad es.una NpgsqlException, almeno sai che è un problema del database.
Quindi, come arrivare all'intera catena di eccezioni di questo evento? È possibile o devo progettare la gestione delle eccezioni in un altro modo?
Nota che, in un certo senso, ho un soluzione alternativa utilizzando Application.SetUnhandledExceptionMode, ma questo è tutt'altro che ideale perché dovrei eseguire il mio ciclo di messaggi.
MODIFICARE:per evitare ulteriori errori, il metodo GetBaseException() NON fa quello che voglio:restituisce semplicemente l'eccezione più interna, mentre l'unica cosa che ho già è l'eccezione più interna.Voglio arrivare all'eccezione più esterna!
Soluzione
Questa domanda è formulata in modo più utile e ha ricevuto risposta qui:
Altri suggerimenti
Ho provato a riprodurre questo comportamento (ottenendo sempre l'eccezione più interna),
ma ottengo l'eccezione che mi aspetto, con tutte le InnerException intatte.
Ecco il codice che ho usato per testare:
Private Shared Sub Test1()
Try
Test2()
Catch ex As Exception
Application.OnThreadException(New ApplicationException("test1", ex))
End Try
End Sub
Private Shared Sub Test2()
Try
Test3()
Catch ex As Exception
Throw New ApplicationException("test2", ex)
End Try
End Sub
Private Shared Sub Test3()
Throw New ApplicationException("blabla")
End Sub
Private Shared Sub HandleAppException(ByVal sender As Object, ByVal e As ThreadExceptionEventArgs)
...
End Sub
Sub HandleAppException gestisce Application.ThreadException.Il metodo Test1() viene chiamato per primo.
Questo è il risultato (e As ThreadExceptionEventArgs) che ottengo in HandleAppException:
ThreadException http://mediasensation.be/dump/?download=ThreadException.jpg
Se catturi e (ri)lanci semplicemente eccezioni, non verrà visualizzata alcuna InnerException, ma verrà aggiunta all'eccezione.StackTrace, come questo:
in SO.Test3() in Test.vb:riga 166
in SO.Test2() in Test.vb:riga 159
in SO.Test1() in Test.vb:riga 151
Normalmente, si perde l'intera catena di eccezioni ad eccezione dell'eccezione di base in un gestore di eccezioni Application.ThreadException se l'eccezione si è verificata in un altro thread.
Dal Libreria MSDN:
Questo evento consente all'applicazione di Windows di gestire eccezioni altrimenti non gestite che si verificano Moduli Windows discussioni.Allega i gestori di eventi all'evento ThreadException per affrontare queste eccezioni, che lascerà la tua domanda in uno stato sconosciuto.Ove possibile, le eccezioni dovrebbero essere gestite da un blocco di gestione delle eccezioni strutturate.
Soluzione:Se esegui il threading, assicurati che tutti i thread/chiamate asincrone siano in un blocco try/catch.O come hai detto, puoi giocare con Application.SetUnhandledExceptionMode.
Ho appena scoperto qualcosa di interessante.Diversi eventi della GUI ti daranno risultati diversi.Un'eccezione generata da un gestore eventi Form.Shown comporterà che Application.ThreadException catturi l'eccezione più interna, ma lo stesso identico codice eseguito nell'evento Form.Load comporterà l'errore più esterno eccezione che viene catturata in Application.ThreadException.
Hai provato il metodo Exception.GetBaseException?Ciò restituisce l'eccezione che ha creato Application.TreadException.Potresti quindi utilizzare lo stesso processo per risalire la catena per ottenere tutte le eccezioni.
Sulla base di alcune informazioni in questa catena, ho utilizzato UnhandledExceptionMode.ThrowException anziché UnhandledExceptionMode.CatchException.Quindi catturo l'eccezione all'esterno di Run() del modulo e questo mi fornisce l'intera catena di eccezioni.
Secondo la documentazione MSDN:
Quando sottoposto a override in una classe derivata, restituisce l'eccezione che è la causa principale di una o più eccezioni successive.
Public Overridable Function GetBaseException() As Exception
Dim innerException As Exception = Me.InnerException
Dim exception2 As Exception = Me
Do While (Not innerException Is Nothing)
exception2 = innerException
innerException = innerException.InnerException
Loop
Return exception2
End Function
Potresti usare una variazione su questo per analizzare la catena di eccezioni.
Public Sub LogExceptionChain(ByVal CurrentException As Exception)
Dim innerException As Exception = CurrentException.InnerException
Dim exception2 As Exception = CurrentException
Debug.Print(exception2.Message) 'Log the Exception
Do While (Not innerException Is Nothing)
exception2 = innerException
Debug.Print(exception2.Message) 'Log the Exception
'Move to the next exception
innerException = innerException.InnerException
Loop
End Sub
Questo mi sembrerebbe esattamente quello che stai cercando.