在WPF应用程序中全局捕获异常?

我们有一个WPF应用程序,它的某些部分可能会在运行时抛出异常。我想全局捕获任何未处理的异常并记录它们,但如果没有发生任何事情,则继续执行程序(有点像VB的On Error Resume Next)。

这在c#中可行吗?如果是这样,我究竟需要把异常处理代码放在哪里?

目前,我看不到任何一个可以包裹try/catch的点,并且它将捕获可能发生的所有异常。即使那样,我也会因为捕获而留下任何已经执行的东西。还是我想错了方向?

埃塔:因为下面很多人指出:应用程序不是为了控制核电站。如果它崩溃了,这不是什么大问题,但它会抛出随机异常,这些异常大多与ui相关,在使用它的上下文中是一个麻烦。有(可能仍然是)一些这样的,因为它使用插件架构,可能被其他人(也可以是学生)扩展;所以没有经验丰富的开发人员,能够编写完全无错误的代码)。

至于捕捉到的异常:我会将它们记录到日志文件中,包括完整的堆栈跟踪。这就是练习的意义所在。只是为了对抗那些把我的类比和VB的OERN类比的人。

我知道盲目忽略某些类型的错误是危险的,可能会破坏我的应用程序实例。如前所述,这个项目对任何人来说都不是关键任务。没有哪个头脑正常的人会把人类文明的存亡赌在这上面。它只是一个测试某些设计方法的小工具。软件工程。

对于应用程序的即时使用,在异常上不会发生很多事情:

  • 没有异常处理-错误对话框和应用程序退出。实验必须重复,尽管可能是另一个实验对象。不幸的是,没有记录任何错误。
  • 泛型异常处理——捕获良性错误,不会造成伤害。从我们在开发过程中看到的所有错误中判断,这应该是常见的情况。忽略这类错误不会产生直接后果;核心数据结构经过了很好的测试,可以很容易地通过这种测试。
  • 通用异常处理-严重错误被捕获,可能在稍后崩溃。这种情况可能很少发生。到目前为止我们还没见过。无论如何,错误都会被记录下来,崩溃可能是不可避免的。这在概念上与第一种情况相似,除了我们有一个堆栈跟踪。在大多数情况下,用户甚至不会注意到。

对于程序生成的实验数据:如果出现严重错误,最坏的结果是没有数据记录。细微的变化,改变实验的结果是非常不可能的。即使在这种情况下,如果结果可疑,错误也会被记录下来;如果它是一个完全的异常值,人们仍然可以抛弃这个数据点。

总而言之:是的,我认为自己至少还是部分清醒的,我不认为让程序运行的全局异常处理例程一定是完全邪恶的。如前所述,这种决定可能是有效的,这取决于应用程序。在这种情况下,它被认为是一个有效的决定,而不是完全的胡说八道。但是请不要仅仅因为我们忽略了错误就指责我或其他为这个项目工作的人可能会炸毁世界。

旁注:该应用程序只有一个用户。它不像Windows或Office那样被数百万人使用,在这些地方,有异常的成本对用户来说已经非常不同了。

128923 次浏览

比如“VB的下一个错误简历?”听起来有点可怕。第一个建议是不要这么做。第二个建议是不要去做,不要去想。你需要更好地找出自己的缺点。至于如何处理这个问题,这取决于代码的结构。如果您正在使用MVC之类的模式,那么这应该不会太难,也绝对不需要全局异常吞咽器。其次,寻找一个好的日志库,如log4net或使用跟踪。我们需要知道更多的细节,比如您所谈论的异常类型,以及应用程序的哪些部分可能导致抛出异常。

使用Application.DispatcherUnhandledException Event。有关摘要,请参阅这个问题(参见德鲁·诺克斯的回答)。

请注意,仍然会有一些异常阻止成功恢复应用程序,例如在堆栈溢出、内存耗尽或在尝试保存到数据库时失去网络连接之后。

除了这里提到的其他内容,请注意将Application.DispatcherUnhandledException(及其相似导线)与

<configuration>
<runtime>
<legacyUnhandledExceptionPolicy enabled="1" />
</runtime>
</configuration>

app.config中将防止你的辅助线程异常关闭应用程序。

应用程序域中。UnhandledException事件

此事件提供未捕获异常的通知。它允许 应用程序在系统之前记录有关异常的信息 默认处理程序将异常报告给用户并终止 应用程序。< / p >

   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);
}

如果UnhandledException事件在默认应用程序中被处理 域,对于任何线程中任何未处理的异常都会引发, 不管线程是从哪个应用程序域中开始的。如果线程 在具有事件处理程序的应用程序域中启动 UnhandledException,则在该应用程序域中引发事件。如果 该应用程序域不是默认应用程序域,并且 在默认应用程序域中还有一个事件处理程序 事件在两个应用程序域中引发 例如,假设一个线程在应用程序域“AD1”中启动, 调用应用程序域“AD2”中的方法,并从那里调用一个方法 方法,该方法在应用程序域“AD3”中抛出异常。的 第一个可以出现UnhandledException事件的应用程序域 凸起的是“AD1”。如果该应用程序域不是默认的 应用程序域,该事件也可以在默认情况下引发 应用程序域。< / p >

下面是使用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);
}
}




}

使用NLog的示例代码,将捕获从AppDomain中的所有线程UI分派器线程异步函数抛出的异常:

App.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);
}
}