17

在 .NET 中,默认的异常处理程序会让用户继续运行程序。但是,我想要一个全局异常处理程序,将堆栈跟踪保存到“errorlog.txt”文件中,以便用户可以将其发送给我,而不必记住单击“详细信息”并将其复制出来对话框(并删除有关已加载程序集等的所有无用废话)。但是当我这样做时,代码不知道如何继续,所以我所能做的就是退出应用程序。有没有办法两全其美?(是的,我知道我所要求的本质上是带有日志记录的“On Error Resume Next”,但我真的认为它会很有用!)

4

7 回答 7

33
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);

如果您在应用程序启动时将自己绑定到此事件,您应该能够捕获应用程序抛出的任何未处理异常,并将其保存到文件中。(使用Exception对象部分UnhandledExceptionEventArgs。我不相信可以从发生错误的地方恢复。

于 2013-09-24T14:35:31.990 回答
2

您可以编写在每个catch块中调用的全局异常处理程序方法,该方法将堆栈跟踪写入您想要保存的位置。但是您需要try . . . catch为每个需要它们的操作编写块并在每个操作中调用异常处理程序。

您还可以在MyApplication.UnhandledException处理程序中为所有未处理的事件调用该全局异常处理程序方法。但是,在这种情况下,当控制权到达该方法时,程序将不会继续运行。

于 2013-09-24T14:47:12.780 回答
1

在 Winform 应用程序中,您可以将处理程序附加到Application.ThreadException事件(确保在调用之前完成Application.Run())。这将摆脱标准的异常对话框(带有“详细信息”按钮的对话框),并使您能够显示/记录您想要的任何内容。

但是,请记住,这仅适用于 UI 线程中抛出的异常。从后台线程引发的异常将无法从您的处理程序中访问。但是,这些仍然可以被AppDomain.UnhandledException处理程序捕获。

于 2013-09-25T13:41:23.823 回答
1

不,不存在,异常是流控制结构,所以On Error Resume Next是不可能的。

您可以在循环中执行操作,并在出现异常时重试您的逻辑。

然而,KandallFrey 是对的,您不应该将异常用作流控制,仅在异常情况下使用它们。

于 2013-09-24T14:32:47.580 回答
0

在我的公司,我建立了一个异常处理框架,它已经为我服务了 7 年。每个程序集都引用 DLL,每个程序集中的每个方法都有一个 try-catch 块。在捕获中,我基本上必须根据“我希望我的异常处理框架在哪里进行干预,即在外部记录异常数据并通知用户问题?”这个问题做出一个决定。在大多数情况下,这个问题的答案是我希望它在方法被外部调用的情况下进行干预,例如,如果它在事件处理程序或其他委托中。所以这里有一些示例代码:

Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
    Try
        Method1()
    Catch ex As Exception
        BSExceptionHandler.ProcessException(BSExceptionHandler.GetBSException(ex, BSExceptionHandler.GetThisMethodName))
    End Try
End Sub

Private Sub Method1()
    Try
        method2()
    Catch ex As Exception
        Throw BSExceptionHandler.GetBSException(ex, BSExceptionHandler.GetThisMethodName)
    End Try
End Sub

Private Sub method2()
    Try
        Dim x As Integer = CInt("x") 'exception thrown here.
    Catch ex As Exception
        Throw BSExceptionHandler.GetBSException(ex, BSExceptionHandler.GetThisMethodName)
    End Try
End Sub

Throw BSExceptionHandler.GetBSException(ex, BSExceptionHandler.GetThisMethodName)在 中第一次调用时Method2(),原始异常被包装在 aBSException中,并且原始方法名称也被存储(稍后会详细介绍)。对 的任何后续调用BSExceptionHandler.GetBSException(ex, BSExceptionHandler.GetThisMethodName),例如 in Method1(),将识别出异常已经是类型BSException并且只是抛出它而不是重新包装它。最终,它到达调用堆栈的“顶部”(不是真正的顶部,只是我决定通过我的框架处理异常的点——即记录它,通知用户等) - - 在这种情况下, in Button1_Click-- 然后BSExceptionHandler.ProcessException(BSExceptionHandler.GetBSException(ex, BSExceptionHandler.GetThisMethodName))被调用。

我的框架所做的ProcessException是确定系统上配置的报告类型。这默认为“简单”,这意味着用户会得到一个非常通用且干扰最小的对话框,指示已发生问题并指示他们联系 IT 部门。但是,有一个可选的注册表设置将报告类型设置为“详细”或“电子邮件”。我在我的开发机器上使用“详细”,因为它在对话框中包含所有异常信息,所以我不必去查看日志。“电子邮件”选项用于在没有用户登录时运行应用程序的服务器上;在这种情况下,错误信息会通过电子邮件发送给 IT 部门,以提醒他们该问题。

无论报告类型如何,从这里发生的另一件事是错误信息被记录到 Windows 事件日志中。这是一个例子:

SILENT: No

ROOT ASSEMBLY: C:\Users\roryap\AppData\Local\Temporary Projects\WindowsApplication1\bin\Debug\WindowsApplication1.exe

DESCRIPTION: BromsunExceptionHandling.BSException: Calling Method 'WindowsApplication1.Form1.method2()' produced 'System.FormatException' exception with message 'Conversion from string "x" to type 'Integer' is not valid.'.

CALLING METHOD: WindowsApplication1.Form1.method2()

STACK TRACE:    at Microsoft.VisualBasic.CompilerServices.Conversions.ToInteger(String Value)
   at WindowsApplication1.Form1.method2() in C:\Users\roryap\AppData\Local\Temporary Projects\WindowsApplication1\Form1.vb:line 32

CALL STACK: WindowsApplication1.Form1.method2()
WindowsApplication1.Form1.Method1()
WindowsApplication1.Form1.Button1_Click(sender As System.Object, e As System.EventArgs)

SOURCE: Microsoft.VisualBasic

TARGET SITE: Int32 ToInteger(System.String)

EXTRA INFO: 

当用户向 IT 报告问题时,IT 所要做的就是检查他们的事件日志以获取错误信息。此外,应用程序不会退出;它继续运行,因为它绝不允许异常在您选择使用处理框架处理异常的点之外冒泡。一旦它被框架处理,它就完成了。

虽然这种方法有一些缺点,但我发现它是一个非常强大的系统,多年来它为我节省了许多小时的盲目故障排除。我已经创建了片段,因此我所要做的就是创建一个 try-catch 块,然后在 catch 块中键入片段快捷方式——例如“pbs”或“tbs”,然后按 Tab 键以填充适当的异常处理方法。因此,在每个方法中使用这个框架是相当轻松的。我什至修改了我的 VS 项目模板以始终引用和包含框架,因此我不必为每个新项目都这样做。

因此,关于GetThisMethodName()函数:此方法使用System.Diagnostics.StackTraceSystem.Diagnostics.StackFrameSystem.Reflection.MethodBase来确定原始异常被包装在 BSException 中的方法的名称。

于 2013-09-24T15:09:41.297 回答
0

My.Application.UnhandledException用来打开一个对话框,用户可以在其中保存有关异常的所有信息。

当表格关闭时,我打电话给e.ExitApplication = False.

于 2013-09-24T14:37:09.223 回答
0

在主构造函数中,将其放入并将在全局范围内执行:

AppDomain.CurrentDomain.FirstChanceException += (sender, eventArgs) =>
{
   MessageBox.Show(eventArgs.Exception.ToString());
};
于 2019-02-18T08:31:22.583 回答