我正在寻找一些有关处理事件异常的最佳实践的指南。目前,当我的应用程序中引发异常时,异常消息会显示在弹出对话框中,然后应用程序在单击“确定”后重新启动。我看到的问题是,一些第三方库的事件处理程序中发生了很多异常,并且这些异常被吞没并且永远不会显示,因为它们位于后台线程上。以下是不同的人想到的一些解决方案,我想知道其中任何一个都是最好的方法。
- 将后台线程转发到应用程序中每个事件处理程序中的 UI 线程。
- 将事件包装在另一个类中,该类在调用事件的每个方法周围都有一个 try/catch。如果发生异常,catch 会将异常转发到 UI 线程。
- 访问第三方库并将 try/catch 放在调用事件的位置,然后可以通过全局事件将其转发到主应用程序。
I'm looking for some guidance on best practices on handling exceptions in events. Currently, when exceptions are thrown in my application, the exception message is displayed in a pop up dialog and then the application restarts after hitting ok. The issue I'm seeing is that a lot of exceptions are occurring in the event handlers of some third party libraries and these exceptions are swallowed and never displayed since they are on a background thread. Here are a few solutions that various people have thought of and I would like to know of any of these are the best approach.
- Forward the background thread to the UI thread in every event handler in the application.
- Wrap the events in another class which has a try/catch around every method that invokes the event. The catch will then forward the exception to the UI thread if one occurs.
- Get access to the third party libraries and put try/catch around where the events are being invoked, which could then be forwarded to the main application by a global event.
发布评论
评论(3)
让我们按照您列出的方式讨论您的选项:
你不应该也不能!因为:
或计时器。
要冻结 UI,您将通过长执行代码来困扰用户界面。
这是一个糟糕的选择,实际上,您不应该更改第三方库的代码即使可以。因为:
那么你必须做什么?答案是您当前正在做的某种事情:
每当抛出任何异常时,记录异常并重新启动您的流程,并在流程中的某个时刻将记录的错误发送到您的电子邮件,然后开始修复它们。
Lets discuss your options the way you listed them:
You should not and you can't! because:
or timer.
to freeze the UI, you will then struggle the user interface by a long execution codes.
This is a bad choice, really, You should not change that code of third party libraries even if you can. because:
So what you have to do? The answer is some sort of what you are currently doing:
Whenever any exception thrown, log the exception restart your process, and at some point in your process send the logged bugs to your email and then start fixing them.
以上都不是。相反,将
Application
和AppDomain
上的事件挂钩以获取未处理的异常。更多信息: WPF 应用程序的全局异常处理事件
Application.DispatcherUnhandledException
仅在 main 上抛出的异常时触发UI 线程。但是,AppDomain.CurrentDomain.UnhandledException
应该在任何线程中的任何未处理异常上触发(不幸的是,无法阻止应用程序在到达此处后关闭)。对最佳实践进行了一些快速研究,发现建议您使用 try\catch 块手动处理后台线程上的异常。阅读此页面上的异常部分 http://www.albahari.com/threading/ 并且看看这个StackOverflow问题捕获未处理的异常单独的线程
None of the above. Instead hook up the events on the
Application
andAppDomain
for unhandled exceptions.Further Information: The global exception handling event for WPF applications
Application.DispatcherUnhandledException
fires only for exceptions thrown on the main UI thread. However theAppDomain.CurrentDomain.UnhandledException
should fire on any unhandled exception in any thread (unfortunately there is no preventing the application from shutting down after ward it reaches here).Did some quick research on best practices and found that it is recommend that you manually handle exceptions on the background threads with
try\catch
block. Read the exception part on this page http://www.albahari.com/threading/ and also take a look at this StackOverflow question Catching unhandled exception on separate threads如果您挂钩 AppDomain.UnhandledException 事件,那么问题并不是它们可能在后台线程上回调,而是第三方库显式吞咽了处理程序引发的异常。这是一个行为不良的库,但是,由于您正在编写事件处理程序代码,因此您可以捕获事件处理程序中的异常并强制关闭应用程序。
由于第三方库无法通过抛出异常来停止,因此可以通过调用以下方法强制线程终止:
Thread.CurrentThread.Abort();
Thread.Abort()< /code> 通常被认为是一种不好的做法,但在这里它的危险性要小一些,因为您正在中止自己,因此不会将异常注入到潜在的令人讨厌的位置(您还知道该线程不在非托管上下文中,因此它将立即中止。)这仍然是一个令人讨厌的黑客行为,但第三方应用程序并没有给你太多选择。 ThreadAbortException 无法“停止”,因此第三方代码一旦到达其异常处理程序的末尾,就会终止其线程(即使它们试图吞掉它)。如果第三方库真的很讨厌,它可能会在其 catch 或 finally 块中调用大量代码,甚至使用肮脏的技巧来保持活动状态,例如启动其他线程,但这样做必须非常恶意。
要获得友好的错误消息行为,您可以使用 SynchronizationContext 或 Dispatcher 将异常编组到 UI 线程,以调用异常处理程序显示(最简单的方法可能是重新抛出异常;我建议将其嵌入为 InnerException这样您就不会丢失堆栈跟踪。)
如果您确实不信任该库(并且不想给它机会即使在其 catch 和 finally 块中也能保持活动状态),您可以使用以下命令显式编组到 UI 线程:阻止调用(以友好的方式显示错误消息),然后调用Environment.FailFast()来终止您的进程。这是一种特别严厉的终止方式(它不会调用任何清理代码,并且会尽快地销毁进程),但如果应用程序状态变得非常损坏,它可以最大限度地减少任何潜在破坏性副作用的可能性。这里需要注意的一件事是,UI 线程可能会被与第三方代码相关的其他一些 UI 调用阻塞。如果发生这种情况,应用程序将陷入死锁。同样,这对它来说是一件非常恶意的事情,所以你可以忽略它。
If you're hooking the AppDomain.UnhandledException event, then the issue isn't the fact that they might be calling back on a background thread, but rather than the third-party library is explicitly swallowing exceptions thrown by the handlers. This is a badly behaving library, but, since you're writing the event handler code, you can catch the exceptions in the event handler and force the application to shut down.
Since the third-party library can't be stopped by throwing an exception, you can force the thread to terminate by calling:
Thread.CurrentThread.Abort();
Thread.Abort()
is generally considered a bad practice, but it's slightly less dangerous here, as you are aborting yourself and therefore won't be injecting the exception into potentially nasty locations (you also know that the thread is not in an unmanaged context, so it will abort right away.) It's still a nasty hack, but the third-party application isn't giving you much choice. ThreadAbortException cannot be 'stopped', so the third-party code will terminate its thread as soon as it reaches the end of its exception handlers (even if they try to swallow it). If the third-party library is really nasty, it might invoke lots of code in its catch or finally blocks, even using dirty tricks to stay alive like launching other threads, but it would have to be pretty malicious to do this.To get the friendly error message behavior, you can marshal the Exception to your UI thread using a SynchronizationContext or Dispatcher to call your exception-handler display (the easiest way may be to just re-throw the exception; I recommend embedding it as an InnerException so that you don't lose stack trace.)
If you really distrust the library (and don't want to give it the opportunity to stay alive even through its catch and finally blocks), you can explicitly marshal to the UI thread with a blocking invoke (to display your error message in a friendly way) and then call
Environment.FailFast()
to kill your process. This is a particularly harsh way to terminate (it will not call any cleanup code and literally tears down the process as quickly as possible), but it minimizes the possibility for any potentially damaging side effects if application state has become very corrupted. One possible thing to watch for here is that the UI thread could be blocked by some other UI invocation tied to the third-party code. If this happens, the application will deadlock. Again, this would be a pretty malicious thing for it to do, so you can probably ignore it.