Globally catch exceptions in a WPF application?
Solution 1
Use the Application.DispatcherUnhandledException Event
. See this question for a summary (see Drew Noakes' answer).
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.
Solution 2
Example code using NLog that will catch exceptions thrown from all threads in the AppDomain, from the UI dispatcher thread and from the async functions:
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);
}
}
Solution 3
AppDomain.UnhandledException Event
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.
Solution 4
In addition what others mentioned here, note that combining the Application.DispatcherUnhandledException
(and its similars) with
<configuration>
<runtime>
<legacyUnhandledExceptionPolicy enabled="1" />
</runtime>
</configuration>
in the app.config
will prevent your secondary threads exception from shutting down the application.
Solution 5
Here is complete example using 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);
}
}
}
Related videos on Youtube
Rich
Software developer at yWorks GmbH. Usually I'll answer questions here about Java, C#, PowerShell, batch files, random numbers, regular expressions and Unicode. At work I primarily deal with C#, Java and JavaScript. My interests are mostly HCI, Interaction Design and UX. Recently I18n joined that list as well. That does not mean I have profound knowledge in either of those fields, just that I am interested in them. Note: People contacting me outside SO/SE is fine, but for discussing their own questions or answers SO already provides a mechanism, namely edits and comments. So please use those things first. I do hang around on Freenode as Hypftier, though.
Updated on March 28, 2020Comments
-
Rich about 4 years
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
).Is this possible in C#? And if so, where exactly would I need to put the exception handling code?
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?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 no experienced developers that are able to write completely error-free code).
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.
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.
-
Russ over 14 yearsThis not really thinking--yes, you probably want the application to exit. BUT, wouldn't be nice to first log the exception with its StackTrace? If all you got from the user was, "Your application crashed when I pressed this button", you may never be able to resolve the issue because you wouldn't have sufficient information. But if you first logged the exception before more pleasantly aborting the application, you will have significantly more information.
-
Rich over 14 yearsI elaborated that point a little in the question now. I know the risks involved and for that particular application it was deemed acceptable. And aborting the application for something as simple as an index out of bounds while the UI tried to do some nice animation is overkill and unneeded. Yes, I don't know the exact cause but we have data to back up the claim that the vast majority of error cases is benign. The serious ones we're masking might cause the application to crash but that's what would have happened without global exception handling anyway.
-
lahjaton_j almost 8 yearsAnother side note: if you prevent any crashes with this approach, the users will most likely love it.
-
Sam Hobbs over 7 yearsSee Windows Handling Unhandled Exceptions in WPF (The most complete collection of handlers) sample in C# for Visual Studio 2010. It has 5 examples including AppDomain.CurrentDomain.FirstChanceException, Application.DispatcherUnhandledException and AppDomain.CurrentDomain.UnhandledException.
-
mike almost 4 yearsI would like to add that VB-like code-flow of
On Error Resume Next
is not possible in C#. After anException
(C# doesn't have "errors") you cannot simply resume with the next statement: execution will continue in acatch
block - or in one of the event handlers described in the answers below. -
Rich almost 4 years@mike: Hence »kinda«.
-
Rich about 15 yearsThe point is that I want to keep the application running even after uncaught exceptions. I just want to log them (we have a custom logging framework for this, based on our needs) and don't need to abort the whole program just because some plugin did something weird in its user interaction code. From what I've seen so far it's not trivial to isolate the causes cleanly as the different parts do not really know each other.
-
Rich about 15 yearsThanks. And yes, I am aware that there are exceptions from which I can't recover, but the vast majority of those that could happen are benign in this case.
-
BobbyShaftoe about 15 yearsI understand but you have to be very cautious of continuing after an exception that you do not examine, there is the possibility of data corruption and so forth to consider.
-
Admin over 14 yearsI agree with BobbyShaftoe. This is not the correct way to go. Let the application crash. If the fault is on some plugin, then fix or get somebody to fix the plugin. Leaving it running is EXTREMELY dangerous. You will get weird side effects and will reach a point where you won't be able to find a logical explanation for the bugs happening in your application.
-
Rich over 14 yearsI elaborated that point a little in the question now. I know the risks involved and for that particular application it was deemed acceptable. And aborting the application for something as simple as an index out of bounds while the UI tried to do some nice animation is overkill and unneeded. Yes, I don't know the exact cause but we have data to back up the claim that the vast majority of error cases is benign. The serious ones we're masking might cause the application to crash but that's what would have happened without global exception handling anyway.
-
Szymon Rozga over 14 yearsIf your exception occurs on a background thread (say, using ThreadPool.QueueUserWorkItem), this will not work.
-
David Schmitt over 14 years@siz: good point, but those can be handled with a normal try block around in the workitem, no?
-
Piti Ongmongkolkul almost 11 yearsBut, how do we resume from using the handlers?
-
David Schmitt almost 11 years@PitiOngmongkolkul: The handler is called as event from you main loop. When the event handlers returns, your app continues normally.
-
Piti Ongmongkolkul almost 11 yearsIt seems that we need to set e.Handled = true, where e is an DispatcherUnhandledExceptionEventArgs, to skip the default handler which quit the programs. msdn.microsoft.com/en-us/library/…
-
Mauro Sampietro almost 9 years@PitiOngmongkolkul The answer should be edited to take into account e.Handled =true
-
George Birbilis over 8 yearscopying from the URL you pointed to (which speaks of console apps and WinForms apps only I think): "Starting with the .NET Framework 4, this event is not raised for exceptions that corrupt the state of the process, such as stack overflows or access violations, unless the event handler is security-critical and has the HandleProcessCorruptedStateExceptionsAttribute attribute."
-
CharithJ over 8 years@GeorgeBirbilis: You can subscribe for UnhandledException event in the App constructor.
-
Chris Schaller over 7 yearsThis solution on it's own may not be enough to catch all exceptions. It is very likely that background threads could fail in a bad enough way to crash your app, I recommend also implementing @charithj solution with AppDomain.UnhandledException or using LegacyUnhandledExceptionPolicy...
-
Nikola Jovic about 6 yearsThis is most complete answer here! Taks scheduler exceptions are included. Best for me, clean and simple code.
-
heltonbiker almost 6 yearsRecently I had an App.config corruption at a customer, and her App wasn't even starting because NLog was trying to read from App.config and threw an exception. Since that exception was in the static logger initializer, it wasn't being caught by the
UnhandledException
handler. I had to look at Windows Event Log Viewer to find what was happening... -
Konrad almost 5 years@ChrisSchaller you can't mark it as handled there though.
-
Chris Schaller almost 5 yearsThanks @Konrad that is likely to be true, but can you not just ignore the exception, in my experience unless this is a memory seek violation, or involving COM+ or Win32 APIs, then the act of catching the exception is normally enough to allow the main UI thread to continue. At least by catching the exception you can log or analyse it, find out what the root cause is and fix that issue, or more aggressively catch the exception closer to the source, allowing your app to continue in the future. Think of it as a "whoops" kind of fail-safe, once you have the exception, then you will know what to do.
-
Konrad almost 5 years@ChrisSchaller that's true, I only globally handle HTTP exceptions (connection problem, etc.) to avoid boilerplate in my code.
-
Apfelkuacha over 4 yearsI reccomend to set
e.Handled = true;
at theUnhandledException
that the application will not crash on UI Exceptions -
Josh Noe about 4 yearsThis should be a comment, not an answer. Responding to "how do I do this" with "you probably shouldn't" is not an answer, it's a comment.
-
Jesse Chisholm over 3 yearsConversely, you could put each plugin into its own app-domain, so you could catch and log its exceptions, then let that one domain crash without crashing your whole app. BUT it does mean trickier plugin<=>app communication. In most cases, the benefit is not worth the extra work.