I'm developing a light-weight WPF MVVM framework, and would like to be able to catch unhandled exceptions, and ideally recover from them.
Ignoring for the moment all the good arguments for not doing this, I encounter the following situation:
If I register a handler for AppDomain.CurrentDomain.UnhandledException within the OnStartup method of the App.xaml.cs, as follows...
App.xaml.cs:
protected override void OnStartup(StartupEventArgs e)
{
AppDomain.CurrentDomain.UnhandledException += new
UnhandledExceptionEventHandler(this.AppDomainUnhandledExceptionHandler);
base.OnStartup(e);
}
void AppDomainUnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs ea)
{
Exception e = (Exception)ea.ExceptionObject;
// log exception
}
and then raise an exception within one of my VM's, the handler is called as expected.
So far so good, except for the fact that there is no way that I can recover using this approach, all that I can do is log the exception and then let the CLR terminate the app.
What I actually wanted to do is to recover, and return control to the main framwork VM. (Again putting aside the motivations against doing this).
So, doing some reading, I decide to register an event handler for AppDomain.CurrentDomain.UnhandledException in the same place, so that the code now looks something like this...
protected override void OnStartup(StartupEventArgs e)
{
AppDomain.CurrentDomain.UnhandledException +=
new UnhandledExceptionEventHandler(this.AppDomainUnhandledExceptionHandler);
this.DispatcherUnhandledException +=
new DispatcherUnhandledExceptionEventHandler(DispatcherUnhandledExceptionHandler);
base.OnStartup(e);
}
void AppDomainUnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs ea)
{
Exception e = (Exception)ea.ExceptionObject;
// log exception
}
void DispatcherUnhandledExceptionHandler(object sender, DispatcherUnhandledExceptionEventArgs args)
{
args.Handled = true;
// implement recovery
}
The issue is that once I register the handler for this.DispatcherUnhandledException, NEITHER EVENT HANDLER IS CALLED. So registering the DispatcherUnhandledExceptionHandler somehow deactivates the handler for AppDomain.CurrentDomain.UnhandledException.
Does anyone have an approach for catching and recovering from unhandled VM exceptions ?
It might be important to mention that there is no explicit use of threading in the framework.
The reason VS shows you the exception is because you have set it up like that (either you did this explicitly or - more likely - the defaults in VS configured it like this). You can control what Visual Studio does when it encounters an exception in the debugged code through the Debug->Exceptions menu.
You can even make it break even though you have a catch for it which is quite handy in some cases.
If you're not using multi threading then you should be fine with the DispatcherUnhandledException event since it will catch everything that gets uncaught on the main UI thread.
A quick answer to my own question:
This works...
App.xaml.cs:
protected override void OnStartup(StartupEventArgs e)
{
Application.Current.DispatcherUnhandledException +=
new DispatcherUnhandledExceptionEventHandler(DispatcherUnhandledExceptionHandler);
base.OnStartup(e);
}
void DispatcherUnhandledExceptionHandler(object sender, DispatcherUnhandledExceptionEventArgs args)
{
args.Handled = true;
// implement recovery
// execution will now continue...
}
[Edit: My comments below have nothing to with the implementation, but my specific IDE (Visual Studio) config with respect to exception catching by the IDE. Please see Isak's comments above.]
BUT, and it's a big but, if you're executing from within VisualStudio, then YOU WILL STILL GET THE VS exception notification dialog box popping up, and the DispatcherUnhandledExceptionHandler will only be invoked if you press F5/continue, after which execution will continue as per normal.
If you're running the compiled binary directly, i.e from the command line or via Windows Explorer, then the handler will be invoked as you would expect, without any intermediary popup.
Related
I created uwp app(.Net native) for Windows 10 desktop. I use HockeyApp for collect live crash reports. Stack trace not informative. This is a long-standing problem and it does not have a solution. I tried this but it not working.
I get these exceptions:
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw
Arg_NullReferenceException
Microsoft.HockeyApp.Extensibility.Windows.UnhandledExceptionTelemetryModule.CoreApplication_UnhandledErrorDetected
The parameter is incorrect. The parameter is incorrect.
On my computer the application works stably. Perhaps this is due to the versions of Windows. also I think that this is due to my xaml. It is very important for me to correct these mistakes. But I can not refuse the table.
Any ideas how can I find the source of these errors?
Sorry, this may not be an answer but I don't have enough room in the comments box.
Try the following in your App.xaml.cs. Add a handler to these events and see if they report something back to you about crashes.
public App()
{
UnhandledException += OnUnhandledException;
TaskScheduler.UnobservedTaskException += OnUnobservedException;
InitializeComponent();
}
private static void OnUnhandledException(object sender, UnhandledExceptionEventArgs e)
{
// Occurs when an exception is not handled on the UI thread.
// if you want to suppress and handle it manually,
// otherwise app shuts down.
e.Handled = true;
}
private static void OnUnobservedException(object sender, UnobservedTaskExceptionEventArgs e)
{
// Occurs when an exception is not handled on a background thread.
// ie. A task is fired and forgotten Task.Run(() => {...})
// suppress and handle it manually.
e.SetObserved();
}
In my App.xaml.cs I have events to handle DispatcherUnhandledExceptions (UI thread) and UnhandledException (Non UI thread). When I throw an exception inside dispatcher.Invoke, the dispatcherhandler catches the exception and handles it. I am setting e.Handled to true after logging the exception. Now the strange behaviour, if i throw the exception outside of the dispatcher.Invoke as a regualr statment, control is sent to the dispatcher exception handler,but after the method is processed the app basically loses control but I can tell its still running by looking at the stop button in vs.
one more thing, if i set e.handled to false, control is sent to the non-ui thread exception handler.
So what puzzles me is why is the application locking after handling the exceptiono in the dispatcher handler?
here's my code
private void App_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
{
// log exception here
e.Handled = true;
} // control is lost after executing this block
private void App_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
try
{
Exception ex = e.ExceptionObject as Exception;
// log exception here
}
finally
{
Environment.Exit(-1);
}
}
An UnhandledException means that your application is effectively dead. If you get that event then it is too late to do anything to recover from the scenario. Typically you would use this event only to log errors for debugging purposes and maybe attempt to cleanup some unmanaged resources.
The following article gives a detailed explanation of exception handling in WPF.
It seems like the app is able to resume operation if the exception is raised after the OnStartup Event, if the exception is raised in between, the exception is caught and handled but the main window is never shown yet the debugger shows the app as running.
the closest solution I found is to remove complex logic from the constructor of main window to allow onstartup event to execute and do a lot of loading in my MainWindow load event.
Global meaning "in one place" e.g. not multiple try...catches e.g. on each event handler. Proven meaning "known to work" - I'm aware covering both .NET and other exceptions is not straightforward.
Thanks.
Solution coded from answers below.
Note: this is believed to cover exceptions from additional threads.
static class Program
{
static void MyHandler(Exception e)
{ MessageBox.Show(e.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
static void MyThreadExceptionEventHandler(object sender, ThreadExceptionEventArgs args)
{ MyHandler(args.Exception);
// App continues.
}
static void MyAppExceptionHandler(object sender, UnhandledExceptionEventArgs args)
{ MyHandler((Exception)args.ExceptionObject);
// There follows a OS "needs to close" dialog, terminating app.
}
static void Main()
{
// For UI thread exceptions
Application.ThreadException +=
new ThreadExceptionEventHandler(MyThreadExceptionEventHandler);
// Force all Windows Forms errors to go through our handler.
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
// For non-UI thread exceptions
AppDomain.CurrentDomain.UnhandledException +=
new UnhandledExceptionEventHandler(MyAppExceptionHandler);
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
The default behavior when it displays error dialog on unhandled exception and terminates is a good formula. If you don't like the look and feel of this dialog you can show your own instead but the principle is the same (good example is here):
public static void Main(string[] args)
{
// Add the event handler for handling UI thread exceptions to the event.
Application.ThreadException
+= new ThreadExceptionEventHandler(/* YOUR OWN HANDLER */);
// Set the unhandled exception mode to force all
// Windows Forms errors to go through our handler.
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
// Add the event handler for handling non-UI thread exceptions to the event.
AppDomain.CurrentDomain.UnhandledException +=
new UnhandledExceptionEventHandler(/* YOUR OWN HANDLER */);
// Runs the application.
Application.Run(new /* YOUR MAIN FORM*/);
}
Generally there is no magic 'solution' to exception handling - you have to think about handling specific exceptions before calling any method. There is nothing special about Windows Forms in this regard.
Handle the AppDomain.CurrentDomain.UnhandledException and the Application.ThreadException you should be good!
Use Exception Handling Application Block and exception policy that may be adjusted in config file. I like it but it might be 'too heavy' for someone when considering very small projects.
I have been looking at the Windows API Code Pack 1.1 and have seen a Error sample and would like to integrate it into my Application, the main idea would be for it to show if any error in the application happens, well not any but some that I choose.
How can I program this?
I am using WPF
Thanks
In the constructor of your App class add:
DispatcherUnhandledException += new System.Windows.Threading.DispatcherUnhandledExceptionEventHandler(App_DispatcherUnhandledException);
then add a method to the App class similar to the following:
void App_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
{
e.Handled = true;
if (MessageBox.Show("An unexpected error has occurred. You should exit this program as soon as possible.\n\n" +
"Exit the program now?\n\nError details:\n" + e.Exception.Message,
"Unexpected error", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
Shutdown();
}
You can have an catch block at the top-level of your program that will display the form with relevant error details. Or you can trap unhandled exceptions using the Application.UnhandledException (assuming you are using winforms), Application.ThreadException and AppDomain.UnhandledException.
If you want a message window to show up when any exception occurs, handled or not, then you will either have to explicitly write code in each catch block to show the form, or use something like PostSharp to weave in code that shows the form whenever an exception is thrown.
The following will catch all exceptions and display them in a messagebox:
[STAThread]
public static void Main()
{
Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
Application.Run(new Form1());
}
private static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
{
// work with exception
MessageBox.Show(e.Exception.Message);
}
Do note that if you're heavy into threading, you might want to test its behaviour with a thread-heavy application.
More information here.
This is a follow up to my initial question and I would like to present my findings and ask for corrections, ideas and insights. My findings (or rather interpretations) come from people's answers to my previous question, reading MSDN .NET 3.5 documentation and debugging .NET 3.5 code. I hope this will be of value to someone who was wondering like me how to detect when an application terminates.
Events:
System.AppDomain.CurrentDomain.ProcessExit: raised when process exits, e.g. after the default AppDomain and everything else was unloaded [Total execution time is limited to just 3 seconds!]. For WPF, use System.Windows.Application.Exit instead. For Windows Forms, run code after Application.Run(...) in main method.
System.AppDomain.CurrentDomain.DomainUnload: raised when an AppDomain other than default AppDomain unloads, e.g. when running classes with unit testing frameworks (MbUnit with TestDriven.NET).
System.AppDomain.CurrentDomain.UnhandledException: (if handled in default AppDomain:) raised for any unhandled exception in any thread, no matter what AppDomain the thread started in. This means, this can be used as the catch-all for all unhandled exceptions.
System.Windows.Application.Exit: raised when WPF application (i.e. the default AppDomain) exits gracefully. Override System.Windows.Application.OnExit to take advantage of it.
Finalizers (destructors in C#): run when garbage collector frees unmanaged resources. [Total execution time is limited!].
Order of events:
WPF application: graceful exit
System.Windows.Application.Exit
System.AppDomain.CurrentDomain.ProcessExit
Finalizers
WPF application: unhandled exception
System.AppDomain.CurrentDomain.UnhandledException
MbUnit running inside TestDriven.NET: passed test (graceful exit)
System.AppDomain.CurrentDomain.DomainUnload
Finalizers
MbUnit running inside TestDriven.NET: failed test (unhandled exceptions are handled by MbUnit)
AppDomain.CurrentDomain.DomainUnload
Finalizers
Questions:
Are my interpretations/findings correct?
Do you know of more details that I have
left out? E.g. what is the total
execution time for finalizers?
Do you know of any other events /
ideas that I be aware of?
What events are there and what order do they get raised in other applications, e.g. Windows Forms, Web Service, ASP.NET web site, etc?
Prompted by ssg31415926's question/answer (this question is a bit reversed), there's also Application.SessionEnding which is called when the when the user logs off or shuts down. It is called before the Exit event.
When Dispatcher.BeginInvokeShutdown() is called, Application.Exit will not be called.
The default timeout for a finalizer's execution is 2 seconds.
You write:
System.AppDomain.CurrentDomain.UnhandledException: (if handled in default AppDomain:) raised for any unhandled exception in any thread, no matter what AppDomain the thread started in. This means, this can be used as the catch-all for all unhandled exceptions.
I do not think that this is correct. Try the following code:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace AppDomainTestingUnhandledException
{
class Program
{
static void Main(string[] args)
{
AppDomain.CurrentDomain.UnhandledException +=
(sender, eventArgs) => Console.WriteLine("Something went wrong! " + args);
var ad = AppDomain.CreateDomain("Test");
var service =
(RunInAnotherDomain)
ad.CreateInstanceAndUnwrap(
typeof(RunInAnotherDomain).Assembly.FullName, typeof(RunInAnotherDomain).FullName);
try
{
service.Start();
}
catch (Exception e)
{
Console.WriteLine("Crash: " + e.Message);
}
finally
{
AppDomain.Unload(ad);
}
}
}
class RunInAnotherDomain : MarshalByRefObject
{
public void Start()
{
Task.Run(
() =>
{
Thread.Sleep(1000);
Console.WriteLine("Uh oh!");
throw new Exception("Oh no!");
});
while (true)
{
Console.WriteLine("Still running!");
Thread.Sleep(300);
}
}
}
}
As far as I can tell, the UnhandledException handler is never called, and the thread will just silently crash (or nag at you if you run it in the debugger).
Just add a new event on your main form:
private void frmMain_Load(object sender, EventArgs e)
{
Application.ApplicationExit += new EventHandler(this.WhenItStopsDoThis);
}
private void WhenItStopsDoThis(object sender, EventArgs e)
{
//Program ended. Do something here.
}