Как получить всю цепочку исключений в обработчике событий Application.ThreadException?
Вопрос
Я как раз работал над исправлением обработки исключений в приложении .NET 2.0 и наткнулся на странную проблему с Приложение.ThreadException.
Я хочу иметь возможность перехватывать все исключения из событий, связанных с элементами графического интерфейса (например,button_Click и т. д.).Затем я хочу отфильтровать эти исключения по «фатальности», например.с некоторыми типами исключений приложение должно продолжать работать, а с другими должно завершиться.
В другом приложении .NET 2.0 я узнал, что по умолчанию только в режиме отладки исключения фактически оставляют вызов Application.Run или Application.DoEvents.В режиме выпуска этого не происходит, и исключения приходится «перехватывать» с помощью события Application.ThreadException.
Однако теперь я заметил, что объект исключения, передаваемый в ThreadExceptionEventArgs события Application.ThreadException, всегда является самым внутренним исключением в цепочке исключений..Однако для целей ведения журнала/отладки/проектирования мне действительно нужна вся цепочка исключений.Нелегко определить, какая внешняя система вышла из строя, например, когда вы только что обработали исключение SocketException:когда он завернут, например.NpgsqlException, то, по крайней мере, вы знаете, что это проблема с базой данных.
Итак, как добраться до всей цепочки исключений из этого события? Возможно ли это вообще, или мне нужно спроектировать обработку исключений по-другому?
Обратите внимание, что у меня вроде как есть обходной путь с использованием Application.SetUnhandledExceptionMode, но это далеко от идеала, потому что мне пришлось бы создавать собственный цикл сообщений.
РЕДАКТИРОВАТЬ:чтобы избежать новых ошибок, метод GetBaseException() НЕ делает то, что я хочу:он просто возвращает самое внутреннее исключение, а единственное, что у меня уже есть, — это самое внутреннее исключение.Я хочу добраться до самого крайнего исключения!
Решение
Этот вопрос более полезно сформулирован и дан ответ здесь:
Другие советы
Я пытался воспроизвести это поведение (всегда получая самое внутреннее исключение),
но я получаю ожидаемое исключение со всеми неповрежденными InnerExceptions.
Вот код, который я использовал для тестирования:
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 обрабатывает Application.ThreadException.Первым вызывается метод Test1().
Это результат (e As ThreadExceptionEventArgs), который я получаю в HandleAppException:
ThreadException http://mediasensation.be/dump/?download=ThreadException.jpg
Если вы просто перехватываете и (повторно) генерируете исключения, InnerExceptions не будет отображаться, но они будут добавлены к исключению.Трассировки стека, так:
в SO.Test3() в Test.vb: строка 166
в SO.Test2() в Test.vb: строка 159
в SO.Test1() в Test.vb: строка 151
Обычно вы теряете всю цепочку исключений, за исключением базового исключения в обработчике исключений Application.ThreadException, только если исключение произошло в другом потоке.
Из Библиотека MSDN:
Это событие позволяет приложению Windows Forms обрабатывать иначе невозможные исключения, которые происходят в Формы Windows потоки.Прикрепите свои обработчики мероприятия к событию ThreadException, чтобы справиться с этими исключениями, что оставит ваше приложение в неизвестном состоянии.Там, где это возможно, исключения должны обрабатываться Структурированный блок обработки исключений.
Решение:Если вы используете многопоточность, убедитесь, что все ваши потоки/асинхронные вызовы находятся в блоке try/catch.Или, как вы сказали, вы можете поиграть с Application.SetUnhandledExceptionMode.
Только что обнаружил кое-что интересное.Различные события графического интерфейса дадут вам разные результаты.Исключение, вызванное обработчиком событий Form.Shown, приведет к тому, что Application.ThreadException перехватит самое внутреннее исключение, но тот же код, запущенный в событии Form.Load, приведет к самый крайний исключение попадает в Application.ThreadException.
Вы пробовали метод Exception.GetBaseException?Это возвращает исключение, создавшее Application.TreadException.Затем вы можете использовать тот же процесс, чтобы пройти вверх по цепочке и получить все исключения.
Основываясь на некоторой информации в этой цепочке, я использовал UnhandledExceptionMode.ThrowException вместо UnhandledExceptionMode.CatchException.Затем я перехватываю исключение за пределами Run() формы, и это дает мне всю цепочку исключений.
Согласно документации MSDN:
При переопределении в производном классе возвращает исключение, которое является основной причиной одного или нескольких последующих исключений.
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
Вы можете использовать этот вариант для анализа цепочки исключений.
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
Мне показалось, что это именно то, что вы ищете.