如何在Application.ThreadException事件处理程序中获取整个异常链?
我只是在修复.NET 2.0应用程序中的异常处理,然后偶然发现了Application.ThreadException的一些奇怪问题。
我想要的是能够从GUI元素(例如button_Click等)后面的事件中捕获所有异常。然后,我想根据"致命性"过滤这些异常,例如对于某些类型的异常,应用程序应继续运行,而对于其他异常,则应退出。
在另一个.NET 2.0应用程序中,我了解到,默认情况下,仅在调试模式下,异常实际上会离开Application.Run或者Application.DoEvents调用。在发布模式下不会发生这种情况,必须使用Application.ThreadException事件来"捕获"异常。
但是,现在我注意到,在Application.ThreadException事件的ThreadExceptionEventArgs中传递的异常对象始终是异常链中最里面的异常。出于日志记录/调试/设计的目的,我确实希望整个异常链。要确定哪个外部系统发生故障并不容易,例如当我们仅处理SocketException时:将其包装为例如NpgsqlException,那么至少我们知道这是数据库问题。
那么,如何从该事件中获取整个异常链?甚至有可能还是我需要以其他方式设计我的异常处理?
请注意,我确实使用Application.SetUnhandledExceptionMode采取了一种解决方法,但这并不理想,因为我必须滚动自己的消息循环。
编辑:为防止更多的错误,GetBaseException()方法不执行我想要的操作:它只返回最里面的异常,而我已经拥有的唯一东西就是最里面的异常。我想获得最外层的例外!
解决方案
回答
我们是否尝试过Exception.GetBaseException方法?这将返回创建Application.TreadException的异常。然后,我们可以使用相同的过程上链获取所有异常。
exception.getbaseexception方法
回答
根据MSDN文档:
在派生类中重写时,返回Exception,这是一个或者多个后续异常的根本原因。
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
这将使我完全符合需求。
回答
我试图重现此行为(始终获取最内部的异常),
但我得到了我期望的异常,所有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
子HandleAppException处理Application.ThreadException。首先调用Test1()方法。
这是我在HandleAppException中得到的结果(例如,作为ThreadExceptionEventArgs):
ThreadException http://mediasensation.be/dump/?download=ThreadException.jpg
如果仅捕获和(重新)抛出异常,则不会显示InnerExceptions,但是它将被添加到Exception.StackTrace,如下所示:
at SO.Test3() in Test.vb:line 166 at SO.Test2() in Test.vb:line 159 at SO.Test1() in Test.vb:line 151
回答
通常,如果异常发生在另一个线程中,则仅会丢失Application.ThreadException异常处理程序中基本异常以外的整个异常链。
从MSDN库:
This event allows your Windows Forms application to handle otherwise unhandled exceptions that occur in Windows Forms threads. Attach your event handlers to the ThreadException event to deal with these exceptions, which will leave your application in an unknown state. Where possible, exceptions should be handled by a structured exception handling block.
解决方案:如果要进行线程化,请确保所有线程/异步调用都在try / catch块中。或者如我们所说,我们可以使用Application.SetUnhandledExceptionMode。
回答
这个问题在这里用更有用的措词和答案表示:
为什么内部异常到达ThreadException处理程序而不是实际抛出的异常?
回答
刚刚发现了一些有趣的东西。不同的GUI事件将为我们带来不同的结果。从Form.Shown事件处理程序引发的异常将导致Application.ThreadException捕获最内部的异常,但是在Form.Load事件中运行的完全相同的代码将导致最外部的异常被Application.ThreadException捕获。