C# 在 WPF 应用程序中全局捕获异常?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/793100/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Globally catch exceptions in a WPF application?
提问by Joey
We are having a WPF application where parts of it may throw exceptions at runtime. I'd like to globally catch any unhandled exception and log them, but otherwise continue program execution as if nothing happened (kinda like VB's On Error Resume Next
).
我们有一个 WPF 应用程序,它的一部分可能会在运行时抛出异常。我想全局捕获任何未处理的异常并记录它们,否则继续执行程序,就好像什么都没发生一样(有点像 VB 的On Error Resume Next
)。
Is this possible in C#? And if so, where exactly would I need to put the exception handling code?
这在 C# 中可能吗?如果是这样,我究竟需要将异常处理代码放在哪里?
Currently I can't see any single point where I could wrap a try
/catch
around and which would catch all exceptions that could occur. And even then I would have left whatever has been executed because of the catch. Or am I thinking in horribly wrong directions here?
目前,我看不到任何可以将try
/catch
环绕的点,并且可以捕获所有可能发生的异常。即便如此,我也会因为抓到而离开已经执行的任何东西。或者我在这里思考的方向非常错误?
ETA:Because many people below pointed it out: The application is not for controlling nuclear power plants. If it crashes it's not that much a big deal but random exceptions that are mostly UI-related are a nuisance in the context where it would be used. There were (and probably still are) a few of those and since it uses a plugin architecture and may be extended by others (also students in that case; so noexperienced developers that are able to write completely error-free code).
ETA:因为下面很多人指出:该应用程序不是用于控制核电站。如果它崩溃了,那没什么大不了的,但主要与 UI 相关的随机异常在使用它的上下文中会令人讨厌。有(并且可能仍然是)其中一些,因为它使用插件架构并且可以由其他人扩展(在这种情况下也是学生;所以没有经验的开发人员能够编写完全无错误的代码)。
As for the exceptions that get caught: I do log them to a log file, including the complete stack trace. That was the whole point of that exercise. Just to counter those people that were taking my analogy to VB's OERN too literally.
至于被捕获的异常:我确实将它们记录到日志文件中,包括完整的堆栈跟踪。这就是该练习的全部意义所在。只是为了反驳那些将我的类比与 VB 的 OERN 过于字面的人。
I know that blindly ignoring certain classes of errors is dangerous and might corrupt my application instance. As said before, this program isn't mission-critical for anyone. No-one in their right mind would bet the survival of the human civilization on it. It's simply a little tool for testing certain design approaches wrt. software engineering.
我知道盲目忽略某些类别的错误是危险的,并且可能会损坏我的应用程序实例。如前所述,该程序对任何人来说都不是关键任务。没有人会以人类文明的生存为赌注。它只是一个用于测试某些设计方的小工具。软件工程。
For the immediate use of the application there are not many things that can happen on an exception:
对于应用程序的直接使用,异常不会发生很多事情:
- No exception handling – error dialog and application exit. Experiment has to be repeated, though likely with another subject. No errors have been logged, which is unfortunate.
- Generic exception handling – benign error trapped, no harm done. This should be the common case judged from all errors we were seeing during development. Ignoring this kind of errors should have no immediate consequences; the core data structures are tested well enough that they will easily survive this.
- Generic exception handling – serious error trapped, possibly crash at a later point. This may happen rarely. We've never seen it so far. The error is logged anyway and a crash might be inevitable. So this is conceptually similar to the very first case. Except that we have a stack trace. And in the majority of cases the user won't even notice.
- 无异常处理 - 错误对话框和应用程序退出。必须重复实验,但可能是针对另一个对象。没有记录任何错误,这是不幸的。
- 通用异常处理 - 良性错误被捕获,无害。这应该是从我们在开发过程中看到的所有错误来判断的常见情况。忽略此类错误应该不会立即产生后果;核心数据结构经过充分测试,可以轻松应对。
- 通用异常处理 - 捕获严重错误,稍后可能会崩溃。这可能很少发生。到目前为止,我们从未见过它。无论如何都会记录错误,并且崩溃可能是不可避免的。所以这在概念上类似于第一种情况。除了我们有一个堆栈跟踪。在大多数情况下,用户甚至不会注意到。
As for the experiment data generated by the program: A serious error would at worst just cause no data to be recorded. Subtle changes that change the result of the experiment ever so slightly are pretty unlikely. And even in that case, if the results seem dubious the error was logged; one can still throw away that data point if it's a total outlier.
至于程序生成的实验数据:一个严重的错误,最坏的情况是不会记录任何数据。微小的改变几乎不可能改变实验结果。即使在这种情况下,如果结果看起来可疑,就会记录错误;如果它是一个完全异常值,仍然可以丢弃该数据点。
To summarize: Yes, I consider myself still at least partially sane and I don't consider a global exception handling routine which leaves the program running to be necessarily totally evil. As said twice before, such a decision might be valid, depending on the application. In this case it was judged a valid decision and not total and utter bullshit. For any other application that decision might look different.But please don't accuse me or the other people who worked on that project to potentially blow up the world just because we're ignoring errors.
总结一下:是的,我认为自己至少还是部分理智的,我不认为全局异常处理例程会使程序运行必然是完全邪恶的。如前所述,这种决定可能是有效的,具体取决于应用程序。在这种情况下,它被认为是一个有效的决定,而不是彻头彻尾的胡说八道。对于任何其他应用程序,该决定可能看起来不同。但是请不要仅仅因为我们忽略了错误就指责我或参与该项目的其他人可能会炸毁世界。
Side note: There is exactly one user for that application. It's not something like Windows or Office that gets used by millions where the cost of having exceptions bubble to the user at all would be very different in the first place already.
旁注:该应用程序只有一个用户。它不像 Windows 或 Office 那样被数百万人使用,在这些东西中,让异常冒泡给用户的成本一开始就已经大不相同了。
采纳答案by David Schmitt
Use the Application.DispatcherUnhandledException Event
. See this questionfor a summary (see Drew Noakes' answer).
使用Application.DispatcherUnhandledException Event
. 请参阅此问题的摘要(请参阅Drew Noakes 的回答)。
Be aware that there'll be still exceptions which preclude a successful resuming of your application, like after a stack overflow, exhausted memory, or lost network connectivity while you're trying to save to the database.
请注意,仍有一些异常会阻止您的应用程序成功恢复,例如在您尝试保存到数据库时出现堆栈溢出、内存耗尽或网络连接丢失等情况。
回答by BobbyShaftoe
Like "VB's On Error Resume Next?" That sounds kind of scary. First recommendation is don't do it. Second recommendation is don't do it and don't think about it. You need to isolate your faults better. As to how to approach this problem, it depends on how you're code is structured. If you are using a pattern like MVC or the like then this shouldn't be too difficult and would definitely not require a global exception swallower. Secondly, look for a good logging library like log4net or use tracing. We'd need to know more details like what kinds of exceptions you're talking about and what parts of your application may result in exceptions being thrown.
就像“VB 出错时继续下一步?” 这听起来有点可怕。第一个建议是不要这样做。第二个建议是不要这样做,也不要考虑它。你需要更好地隔离你的错误。至于如何解决这个问题,这取决于你的代码是如何构建的。如果您使用的是 MVC 之类的模式,那么这应该不会太困难,并且绝对不需要全局异常吞食器。其次,寻找一个好的日志库,如 log4net 或使用跟踪。我们需要了解更多细节,例如您所谈论的异常类型以及应用程序的哪些部分可能会导致抛出异常。
回答by Hertzel Guinness
In addition what others mentioned here, note that combining the Application.DispatcherUnhandledException
(and its similars) with
除了这里其他人提到的,请注意将 Application.DispatcherUnhandledException
(及其类似物)与
<configuration>
<runtime>
<legacyUnhandledExceptionPolicy enabled="1" />
</runtime>
</configuration>
in the app.config
will prevent your secondary threads exception from shutting down the application.
在app.config
将阻止您的辅助线程异常关闭应用程序。
回答by CharithJ
AppDomain.UnhandledExceptionEvent
This event provides notification of uncaught exceptions. It allows the application to log information about the exception before the system default handler reports the exception to the user and terminates the application.
此提供未捕获异常的通知。它允许应用程序在系统默认处理程序向用户报告异常并终止应用程序之前记录有关异常的信息。
public App()
{
AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.UnhandledException += new UnhandledExceptionEventHandler(MyHandler);
}
static void MyHandler(object sender, UnhandledExceptionEventArgs args)
{
Exception e = (Exception) args.ExceptionObject;
Console.WriteLine("MyHandler caught : " + e.Message);
Console.WriteLine("Runtime terminating: {0}", args.IsTerminating);
}
If the UnhandledException event is handled in the default application domain, it is raised there for any unhandled exception in any thread, no matter what application domain the thread started in. If the thread started in an application domain that has an event handler for UnhandledException, the event is raised in that application domain. If that application domain is not the default application domain, and there is also an event handler in the default application domain, the event is raised in both application domains.
For example, suppose a thread starts in application domain "AD1", calls a method in application domain "AD2", and from there calls a method in application domain "AD3", where it throws an exception. The first application domain in which the UnhandledException event can be raised is "AD1". If that application domain is not the default application domain, the event can also be raised in the default application domain.
如果 UnhandledException 在默认应用程序域中处理,则任何线程中的任何未处理异常都会在那里引发,无论线程在哪个应用程序域中启动。 如果线程在具有 UnhandledException 处理程序的应用程序域中启动,该是在该应用程序域中引发的。如果该应用程序域不是默认应用程序域,并且默认应用程序域中还有一个处理程序,则在两个应用程序域中都会引发该。
例如,假设一个线程在应用程序域“AD1”中启动,调用应用程序域“AD2”中的方,并从那里调用应用程序域“AD3”中的方,并在那里抛出异常。可以在其中引发 UnhandledException 的第一个应用程序域是“AD1”。如果该应用程序域不是默认应用程序域,则也可以在默认应用程序域中引发该。
回答by Developer
Here is complete example using NLog
这是使用的完整示例 NLog
using NLog;
using System;
using System.Windows;
namespace MyApp
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
private static Logger logger = LogManager.GetCurrentClassLogger();
public App()
{
var currentDomain = AppDomain.CurrentDomain;
currentDomain.UnhandledException += CurrentDomain_UnhandledException;
}
private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
var ex = (Exception)e.ExceptionObject;
logger.Error("UnhandledException caught : " + ex.Message);
logger.Error("UnhandledException StackTrace : " + ex.StackTrace);
logger.Fatal("Runtime terminating: {0}", e.IsTerminating);
}
}
}
回答by Hüseyin Ya?l?
Example code using NLogthat will catch exceptions thrown from all threads in the AppDomain, from the UI dispatcher threadand from the async functions:
示例代码中使用NLOG,将捕获从抛出的异常的AppDomain中的所有线程,从UI线程调度,并从异步功能:
App.xaml.cs :
应用程序.xaml.cs :
public partial class App : Application
{
private static Logger _logger = LogManager.GetCurrentClassLogger();
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
SetupExceptionHandling();
}
private void SetupExceptionHandling()
{
AppDomain.CurrentDomain.UnhandledException += (s, e) =>
LogUnhandledException((Exception)e.ExceptionObject, "AppDomain.CurrentDomain.UnhandledException");
DispatcherUnhandledException += (s, e) =>
{
LogUnhandledException(e.Exception, "Application.Current.DispatcherUnhandledException");
e.Handled = true;
};
TaskScheduler.UnobservedTaskException += (s, e) =>
{
LogUnhandledException(e.Exception, "TaskScheduler.UnobservedTaskException");
e.SetObserved();
};
}
private void LogUnhandledException(Exception exception, string source)
{
string message = $"Unhandled exception ({source})";
try
{
System.Reflection.AssemblyName assemblyName = System.Reflection.Assembly.GetExecutingAssembly().GetName();
message = string.Format("Unhandled exception in {0} v{1}", assemblyName.Name, assemblyName.Version);
}
catch (Exception ex)
{
_logger.Error(ex, "Exception in LogUnhandledException");
}
finally
{
_logger.Error(exception, message);
}
}