Strategies for Handling Errors in your Windows Store Apps
Working in developer support at Microsoft we see a lot of cases in the forums and customers that call in which relate to errors and exceptions in their applications. I think the most frustrating are the ones that cause an application to terminate and leaving the application in a bad state. Then there is the case when you just want to know why the application exited and there appears to be no evidence. This blog will cover some strategies for making sure your application has the chance to put it into a known state when an un-recoverable exception occurs as well as show how to log those exceptions to a file before it exits so there is some trace evidence of what occurred.
Adding the global Exception handler
There is a good article in the documentation for Store apps on exception handling here:
https://msdn.microsoft.com/en-us/library/windows/apps/xaml/dn532194.aspx
The article covers using try/catch as well as catching global exceptions. It also has good insight on when you should handle global exceptions or let them go to the operating system to handle. What I will cover in this article is how to catch those exceptions in the applications UnhandledException function to save application state and log details to a file.
The Application class has an UnhandledException event we can subscribe to. The following code shows how to implement this event in the App.xaml.cs file of a Universal App.
public App() { this.InitializeComponent(); this.Suspending += this.OnSuspending; this.UnhandledException += App_UnhandledException; } void App_UnhandledException(object sender, UnhandledExceptionEventArgs e) { }
Saving application state in the UnhandledException event
Now that we have our UnhandledException event registered we want to do a couple of things in the event. First, we should check to see if we can recover from the exception. Second, if we determine we cannot recover we should save the application state. Determining whether your application should continue or exit will be specific to your application. In my example if a System.ArgumentException is thrown, the application will set e.Handled to true and continue to run. Otherwise it will call SaveAppData and set e.Handled to false causing the exception to get handled by the operating system. The developer should add some logic to the OnLaunched event to decide if the application previously crashed and then restore from the saved state.
private void SaveAppdata() { StorageFolder folder = Windows.Storage.ApplicationData.Current.LocalFolder; Task<StorageFile> tFile = folder.CreateFileAsync("AppData.txt").AsTask<StorageFile>(); tFile.Wait(); StorageFile file = tFile.Result; Task t = Windows.Storage.FileIO.WriteTextAsync(file, "This Is Application data").AsTask(); t.Wait(); } void App_UnhandledException(object sender, UnhandledExceptionEventArgs e) { // In this routine we will decide if we can keep the application running or if we need to save state and exit // Either way we log the exception to our error file. if (e.Exception.GetType() == typeof(System.ArgumentException)) { e.Handled = true; // Keep Running the app } else { SaveAppdata(); e.Handled = false; } }
Writing to a log file in the UnhandledException event
So at this point we can save our application data to a file when the UnhandledException event occurs. Next we will add the ability for the application to log errors to a file when the UnhandledException event occurs. First, we expose a public property from our App class of type StorageFile:
private StorageFile _errorFile; public StorageFile ErrorFile { get { return _errorFile; } set { _errorFile = value; } }
We need a function to create the file, and then call that in the App class constructor:
public App() { this.InitializeComponent(); this.Suspending += this.OnSuspending; CreateErrorFile(); this.UnhandledException += App_UnhandledException; } private async void CreateErrorFile() { try { // Open Error File StorageFolder local = Windows.Storage.ApplicationData.Current.LocalFolder; ErrorFile = await local.CreateFileAsync("ErrorFile.txt", CreationCollisionOption.OpenIfExists); } catch (Exception) { // If cannot open our error file, then that is a shame. This should always succeed // you could try and log to an internet serivce(i.e. Azure Mobile Service) here so you have a record of this failure. } }
Then create a function for writing an error message to our file:
public async Task WriteMessage(string strMessage) { if (ErrorFile != null) { try { // Run asynchronously await Windows.Storage.FileIO.AppendTextAsync(ErrorFile, string.Format("{0} - {1}\r\n", DateTime.Now.ToLocalTime().ToString(), strMessage)); } catch (Exception) { // If another option is available to the app to log error(i.e. Azure Mobile Service, etc...) then try that here } } }
Lastly, we can update our UnhandledException event handler to write to our log file. We can also call App.WriteMessage from anywhere in our application to log information to our Error Log.
void App_UnhandledException(object sender, UnhandledExceptionEventArgs e) { // In this routine we will decide if we can keep the application running or if we need to save state and exit // Either way we log the exception to our error file. if (e.Exception.GetType() == typeof(System.ArgumentException)) { WriteMessage(string.Format("UnhandledException - Continue - {0}", e.Exception.ToString())); e.Handled = true; // Keep Running the app } else { Task t = WriteMessage(string.Format("UnhandledException - Exit - {0}", e.Exception.ToString())); t.Wait(3000); // Give teh application 3 seconds to write to the log file. Should be enough time. SaveAppdata(); e.Handled = false; } }
And that is it. At this point in time your application should be able to save its data before it terminates because of an unhandled exception, and have an ErrorLog in the Applications LocalFolder. What you do with this file is really up to you. If it is something you want from all of your users you can upload it to a custom Azure Service at application startup. If you only want it from users that opt in to sharing information you can create a setting in your application, and when it is enabled your application can upload the file. There are also many other options for monitoring application health, like Application Insightswhich runs in Azure. I hope this blog helps you understand some options you have for dealing with exceptions in your applications. Until next time, have fun coding!
Don’t forget to follow the Windows Store Developer Solutions team on Twitter @wsdevsol. Comments are welcome, both below and on twitter.
- Bret Bentzinger(Microsoft) @awehellyeah
Comments
- Anonymous
November 04, 2014
what about the errors that happens in async code. global exception handler don't run in this event. is there any way to handle those exceptions that does not happen in main application thread?