【问题标题】:Suppressing Application.ThreadException and AppDomain.CurrentDomain.UnhandledException抑制 Application.ThreadException 和 AppDomain.CurrentDomain.UnhandledException
【发布时间】:2014-06-02 21:56:12
【问题描述】:

我编写了一个执行某种同步工作的 WinForms 应用程序。尽管发生任何异常,我都希望它继续运行,以便在下次尝试时继续同步。因此,我编写了这样的应用程序,即每当发生异常时,它都会记录异常堆栈跟踪,执行一些诊断,然后记录诊断信息并继续下一次同步尝试。

对于未捕获的异常,我在Main() 线程中为Application.ThreadExceptionAppDomain.CurrentDomain.UnhandledException 添加了异常处理程序:

static void Main(string[] args)
{    
    Application.ThreadException += new ThreadExceptionEventHandler(UIThreadExceptionHandler);
    Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
    AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(UnhandledExceptionHandler);


    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    Application.Run(new myForm());
}


//exception handlers
 public static void UIThreadExceptionHandler(object sender, ThreadExceptionEventArgs t)
 {
     logger.Fatal("Fatal Windows Forms Error");
     logStackTrace(t.Exception);                    //logs the stack trace with some desired formatting  
     Application.Exit(new CancelEventArgs(true));
 }

 public static void UnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs t) 
 {
     logger.Fatal("Fatal Application Error");
     logStackTrace((Exception)(t.ExceptionObject)); //logs the stack trace with some desired formatting           
     Application.Exit(new CancelEventArgs(true));
 }

我让我的应用程序继续运行以进行测试,并意识到只要捕获到Application.ThreadException,应用程序就会正确记录字符串Fatal Windows Forms Error,然后是异常堆栈跟踪,然后应用程序退出。

我不希望应用退出。我只想让它记录异常堆栈跟踪并继续下一次同步尝试。 UI 线程中发生的异常并不那么严重,因为同样的异常也发生在应用程序的其他/非 UI 线程中,但它并没有使应用程序崩溃(正如我在日志中看到的那样)。但是,当 UI 线程中发生相同的异常时,应用程序会崩溃。

【问题讨论】:

  • 问题是什么?您希望应用在出现未处理的异常后继续工作吗?
  • @Dmitry 是的,显然我在第一段中说过 希望它在发生任何异常的情况下继续运行
  • 请想知道为什么投反对票
  • 为什么要使用 Application.Exit(new CancelEventArgs(true));在 UIThreadExceptionHandler 中?删除它,用户界面不会关闭
  • 糟糕,我想我有一些关于 Application.ThreadExceptionCancelEventArgs 的坏信息。让我试试这个。我认为CancelEventArgstrue 参数应该可以防止应用程序崩溃。我想我已经读过退出应用程序是Application.ThreadException 的默认行为,为了防止我应该这样做Application.Exit(new CancelEventArgs(true));

标签: c# winforms exception unhandled-exception


【解决方案1】:

因为它是 windows 窗体应用程序 Application.ThreadException 用于未处理的异常:“此事件允许您的 Windows 窗体应用程序处理在 Windows 窗体线程中发生的未处理的异常。将您的事件处理程序附加到 ThreadException 事件以处理这些异常" (MSDN)

我认为你必须期待在 UIThreadExceptionHandler 中你有 Application.Exit(new CancelEventArgs(true)) 这样的行为。退出方法描述:“通知所有消息泵它们必须终止,然后在处理完消息后关闭所有应用程序窗口。” (MSDN)

AppDomain.CurrentDomain.UnhandledException 事件用于处理非 UI 线程异常。

编辑 1:

AppDomain.CurrentDomain.UnhandledException 专门设计用于在系统通知用户并终止进程之前记录异常。您无法阻止终止进程(除非您使用的是 Net 1.1)。

Application.ThreadException + UnhandledExceptionMode.CatchException 允许您保持 UI 线程处于活动状态。但是,这真的不是一个好主意。最好将容易出错的代码包含在 try-catch 块中。

因此,如果您想捕获来自线程的异常并保持应用程序处于活动状态,则必须在内部使用 try-catch 块来完成。

UnhandledExceptionHandler 没有问题,因为我想它根本没有被触发。

【讨论】:

  • 所以我应该删除Application.Exit(new CancelEventArgs(true)); 以防止应用程序在Fatal Error 之后退出?我认为CancelEventArgstrue 参数应该可以防止应用程序崩溃。我想我已经读过退出应用程序是Application.ThreadException 的默认行为,为了防止我应该这样做Application.Exit(new CancelEventArgs(true));
  • 是的,您应该删除这行代码。 UI 不会关闭。
  • 刚刚对上面的cmets做了一些改动,请清除我
【解决方案2】:

Windows 窗体并不是真正为应用程序需要无限期运行的场景而设计的。正如您所经历的,当您截获某些异常时,您的应用程序已经被吸入了一个黑洞,您无法阻止您的应用程序死亡。

我认为更好的方法是使用具有两个线程的 Windows 服务:前台监控线程和后台工作线程(使用 BackgroundWorker 类型)。工作线程是您的同步工作发生的地方。当工作线程因未处理的异常而死亡时,监控线程会记录该异常并重新启动工作线程。

如果情况严重到监视器线程也死掉(例如 OutOfMemoryException 或 ExecutionEngineException),那么服务本身也会死掉。但是你可以设置服务控制管理器在这种情况下重启服务。

如果您需要某种用户交互性,您还可以创建一个 Windows 窗体应用程序来与您的新服务对话并启动/停止它。

【讨论】:

  • 但是现在将 WinForms 转换为服务将需要进行相当大的改进。是的,我需要用户交互。我还想知道如何为 Windows 服务提供 UI。但为此我应该写另一个问题。但是你能注意一下绑定 UI 以赢得服务的方法吗?我猜注册中心是一回事?
  • 我也可以在这里做一些技巧。事实上,我认为CancelEventArgs(true) 应该可以解决问题,但似乎没有。此外,发生的异常并不像我在日志中看到的那么严重,因为此异常也发生在其他/非 UI 线程中,但它们不会使应用程序崩溃。但是当它出现在 UI 线程中时,应用会崩溃
猜你喜欢
  • 2011-01-02
  • 1970-01-01
  • 2011-09-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-27
  • 2017-03-30
相关资源
最近更新 更多