是否可以在 C# 中创建一个全局异常处理程序,让代码在之后继续运行

本文关键字:异常处理程序 全局 之后 运行 继续 一个 代码 创建 是否 | 更新日期: 2023-09-27 18:32:47

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

是否可以在 C# 中创建一个全局异常处理程序,让代码在之后继续运行

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

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

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

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

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

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

在主构造函数中,放置这个并将全局执行:

AppDomain.CurrentDomain.FirstChanceException += (sender, eventArgs) =>
{
   MessageBox.Show(eventArgs.Exception.ToString());
};

(注意:如评论中所述,这可能不是线程安全的(。

不,不存在,异常是流控制结构,因此不可能On Error Resume Next

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

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

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

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

在我的公司,我建立了一个异常处理框架,它已经为我服务了 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

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

我的框架ProcessException的作用是确定系统上配置的报告类型。 这默认为"简单",这意味着用户将获得一个非常通用且侵入性最小的对话框,指示已发生问题并指示他们联系 IT 部门。 但是,有一个可选的注册表设置,它将报告类型设置为"详细"或"电子邮件"。 我在开发计算机上使用"verbose",因为它在对话框中包含所有异常信息,因此我不必查看日志。 "电子邮件"选项用于没有用户登录时运行应用程序的服务器上;在这种情况下,错误信息将通过电子邮件发送给 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 块,然后将代码段快捷方式(例如"pbs"或"tbs"(键入到捕获块中,然后按 tab 键填充适当的异常处理方法。 因此,在每种方法中使用此框架都非常轻松。 我什至修改了我的 VS 项目模板以始终引用和包含框架,因此我不必为每个新项目都这样做。

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

对我来说

,这个解决方案:

  1. 在 WebApiConfig 中.cs添加:
public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            config.Services.Replace(typeof(IExceptionLogger), new UnhandledExceptionLogger());
            ...
        }
  1. 创建新文件UnhandledExceptionLogger.cs:
    public class UnhandledExceptionLogger : ExceptionLogger
    {
        public override void Log(ExceptionLoggerContext context)
        {
            File.AppendAllText("log.txt", context.Exception.ToString());
        }
    }