Problem
We have a WPF application in which some components may throw exceptions at runtime. I’d like to catch and log any unhandled exceptions globally, but otherwise continue program execution as if nothing had happened (similar to Visual Basic’s On Error Resume Next).
Is it possible to do this in C#? And, if that’s the case, where should I put the exception handling code?
At the moment, I can’t think of a single place where I could wrap a try/catch around to catch all possible exceptions. And even then, because of the catch, I would have left whatever had been executed. Or am I going in the completely incorrect route here?
ETA: As many commenters below have pointed out, the software is not for operating nuclear power stations. It’s not a major deal if it crashes, but random exceptions, which are largely UI-related, are a pain in the neck in the environment where it would be utilized. There were (and presumably still are) a few of those, and because it employs a plugin architecture and can be modified by others, there were (and probably still are) a few of them (also students in that case; so no experienced developers that are able to write completely error-free code).
Exceptions that are caught are logged to a log file, along with the whole stack trace. That was the entire point of the game. To rebut those who mistook my parallel to VB’s OERN for a literal comparison.
I understand that ignoring certain types of errors is risky and could lead to the corruption of my application instance. This software isn’t mission-critical for anyone, as previously stated. Nobody in their right mind would put their civilization’s survival on it. It’s just a simple tool for evaluating different software engineering design techniques.
There aren’t many things that can go wrong with the application when it’s being used right now:
In terms of the experiment data created by the program, a major error would, at the very least, result in no data being collected. Subtle modifications that alter the experiment’s outcome only slightly are highly unlikely. Even though the findings appear suspicious, the error was logged, and the data point can still be discarded if it’s a complete outlier.
To recap, I believe I am still mostly sane, and I don’t believe a global exception handling function that keeps the application going is necessarily completely wicked. As previously stated, depending on the application, such a decision may be valid. In this situation, the ruling was deemed valid rather than pure and utter nonsense. That decision might be different for another application. But don’t accuse me or the other people who worked on that project of threatening to destroy the world simply because we’re disregarding mistakes.
A side note: That program has only one user. It’s not something like Windows or Office, which are used by millions of people, where the cost of having exceptions bubble up to the user would already be quite different.
Asked by Joey
Solution #1
Make use of the app. Event: DispatcherUnhandledException A summary can be found in this question (see Drew Noakes’ response).
Be careful that there will still be exceptions that prevent your program from successfully resuming, such as a stack overflow, memory exhaustion, or lost network connectivity while trying to save to the database.
Answered by David Schmitt
Solution #2
Using NLog, here’s an example of code that will catch exceptions raised by all threads in the AppDomain, the UI dispatcher thread, and async functions:
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);
}
}
Answered by Hüseyin Yağlı
Solution #3
AppDomain.UnhandledException Event
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);
}
Answered by CharithJ
Solution #4
In addition to what others have said, consider that the Application can be combined. With DispatcherUnhandledException (and similar exceptions),
<configuration>
<runtime>
<legacyUnhandledExceptionPolicy enabled="1" />
</runtime>
</configuration>
in the app.config will prevent the program from being shut down due to a secondary threads exception.
Answered by Hertzel Guinness
Solution #5
The following is a comprehensive NLog example.
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);
}
}
}
Answered by NoWar
Post is based on https://stackoverflow.com/questions/793100/globally-catch-exceptions-in-a-wpf-application