Crash handling in Silverlight (Part One)

This post is the first part of a two part series.

I love working with Silverlight but once in a while I get it wrong and I, or worse, a user experiences a crash incident. I believe it is important for developers to acknowledge that incidents will occur and to include incident handling and reporting as first class citizens in the software we write. It benefits both us and our users by providing both a graceful degradation of our application and a source of feedback regarding application stability and bugs.

In this and subsequent posts, I want to take a look at the functionality a new Silverlight project includes for handling and reporting incidents, and then build upon it to give us some top notch error handling in our Silverlight applications.

So, let's start with the basics1

Create a new Silverlight application (including a companion ASP.NET web application or website) and you'll get two flavours of error handling: the first reports errors when you're not debugging your application and the second reports errors when you are2. When there is no debugger attached, errors are reported to the DOM via the HTML bridge. This all happens in App.xaml.cs via an event handler for the Application.UnhandledException event, which is subscribed in the class constructor. The event handler checks whether the debugger is attached and if it is not, it sends the error to the DOM via the cunningly-named ReportErrorToDOM method. The comments in Application_UnhandledException explain what is happening. Also, note that the event is being marked as handled, meaning our application won't stop running just because of this pesky exception.

private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
{
    // If the app is running outside of the debugger then report the exception using
    // the browser's exception mechanism. On IE this will display it a yellow alert
    // icon in the status bar and Firefox will display a script error.
    if (!System.Diagnostics.Debugger.IsAttached)
    {
        // NOTE: This will allow the application to continue running after an exception has been thrown
        // but not handled.
        // For production applications this error handling should be replaced with something that will
        // report the error to the website and stop the application.
        e.Handled = true;
        Deployment.Current.Dispatcher.BeginInvoke(delegate
        {
            ReportErrorToDOM(e);
        });
    }
}

And a quick look at ReportErrorToDOM shows us the HTML bridge is being used to evaluate some JavaScript that will throw an exception inside the hosting browser.

private void ReportErrorToDOM(ApplicationUnhandledExceptionEventArgs e)
{
    try
    {
        string errorMsg = e.ExceptionObject.Message + e.ExceptionObject.StackTrace;
        errorMsg = errorMsg.Replace('"', '\'').Replace("\r\n";, @"\n");

        System.Windows.Browser.HtmlPage.Window.Eval(
            "throw new Error(\"Unhandled Error in Silverlight Application " + errorMsg + "\");");
    }
    catch (Exception)
    {
    }
}

If you try this and you're using a recent browser, it's very likely that you won't even notice anything happened. I put a button on my vanilla application and tied that to a click-event handler that throws an InvalidOperationException exception. Clicking it in IE9 gave me nothing until I pressed F12, viewed the Console in the developer tools and tried again. In some older browsers, a dialog box may appear indicating a scripting error.

Console in IE9 after a crash using basic Silverlight exception handling
Console in IE9 after a crash using basic Silverlight exception handling

I'll leave it as an exercise for you to go and see exactly what this looks like in your browser of choice, but I think we can all agree that with error handling like this our users must surely feel a warm fuzzy glow (especially with our hidden, cryptic error message and an application that continues running, unfettered by the potentially fatal bug lurking within). In fact, if the HTML bridge is disabled3, there isn't even a cryptic error message!

However, you'll be pleased to read that things are almost ever so very slightly better different when we have the debugger attached.

Attaching the debugger

The exception handler we just looked at only reports the error to the DOM when the debugger is not attached (and the HTML bridge is enabled). When the debugger is attached, the handler does nothing at all and the exception goes unhandled by our application. So what happens next?

Well, when an unhandled exception leaves our application, the Silverlight host looks for a JavaScript method assigned to the onError parameter. If there is such a method, it calls that and stops the application. The default handler is defined for us in the auto-generated HTML and ASPX. You can see its assignment in the default HTML and ASPX pages generated for us when we created our Silverlight application.

<object data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="100%" height="100%">
    <param name="source" value="ClientBin/AgIncidentReporting.xap"/>
    <param name="onError" value="onSilverlightError" />
    <param name="background" value="white" />
    <param name="minRuntimeVersion" value="4.0.50826.0" />
    <param name="autoUpgrade" value="true" />
    <a href="http://go.microsoft.com/fwlink/?LinkID=149156&v=4.0.50826.0" style="text-decoration:none">
        <img src="http://go.microsoft.com/fwlink/?LinkId=161376" alt="Get Microsoft Silverlight" style="border-style:none"/>
    </a>
</object>

The default JavaScript handler generates an error similar to the one we get without the debugger, but with a few extra details4. Unfortunately, just like when the debugger isn't attached, disabling the HTML bridge (or running out-of-browser) also disables the error being reported.

Console in IE9 after a crash using basic Silverlight exception handling with debugger attached
Console in IE9 after a crash using basic Silverlight exception handling with debugger attached

The story so far…

In this introduction to Silverlight incident handling we have seen that, out of the box, we get some basic reporting that in a release scenario, will allow the application to continue running and will output a stack trace in the console5 but, when debugging, will stop the application and output a stack trace plus the added bonus of some extra details5.

I think you will agree, this is not a first class system for handling errors. Of course, all is not lost and in the next post we will look at how we can expand this error handling to be more helpful to both us and our users, perhaps even coping without the HTML bridge. That said, if this is all you use, I recommend deleting the code from App.xaml.cs so that the "debugger attached" style handling is used for all scenarios6. At least that way, when your application crashes, the user won't be able to continue using the application in blissful ignorance of whatever dangers lurk beneath.

  1. More detailed information on error handling in Silverlight, can be found here. []
  2. I am using Visual Studio 2010 Professional SP1 with the Silverlight 4 SDK. All line numbers refer to a vanilla Silverlight project created using New Project in the File menu. []
  3. You can disable the HTML bridge by adding <param name="enableHtmlAccess" value="false" /> to your Silverlight object declaration or by running your application out-of-browser. []
  4. The Category value is only available if the development runtime is installed, but the Code value is provided with all runtimes. It indicates the type of error, which can be looked up here. []
  5. If we're running in a browser with the HTML bridge enabled. [] []
  6. Please don't actually use this as your error handling. Although it is in the most part, better for the user, your application will appear to crash a little more often as the onError handler gets called for more than just unhandled exceptions. []

Firecracker 5K 2011

11th annual Ann Arbor Firecracker 5K banner

This July 4th weekend, we headed downtown for the Firecracker 5K. Congratulations to everyone else who took part, I hope you all achieved your goals. Even though I had completed two other 5K events this year (DXA2 and the Motor City Triathlon as part of a sprint relay team), I had not managed to run the entire 5K distance (3.1 miles) since the Big House Big Heart run in October 2009*, so I had that goal in mind.

The weather was cool and breezy and the course was the same as last year, nice and flat and taking in some of the best parts of downtown Ann Arbor. With coffee helping to keep my eyes open (I'm not fully functional in the morning, especially on holiday weekends), I pinned my bib on, stretched and found my pace marker in the crowd (around 12 minutes†, I was hoping).

After setting out a tad fast it was beginning to feel like I had scuppered my chances of finishing without stopping for a lovely stroll. However, with Cardiotrainer's robot voice giving me pace and distance information over the top of my music (streaming on my phone via the fantastic Audiogalaxy) I was able to focus on my goal and just over 30 minutes later, I was insanely sprinting over the finish line.

My time was 34:01.3, a personal best beating my 2009 Big House Big Heart time of 35:54.6 and my 2011 DXA2 time of 37:39.0 (it also totally blows away my time from last year's Firecracker 5K, 46:50.1). My next goal is to break 30 minutes; when and where that will be I don't yet know.

* I injured myself playing soccer with delusions of skill
† Also know as "somewhere near the back"

Humble beginnings

I saw friends do it, I saw professionals do it and I wondered, "Why don't I do it?"

Ever since I started taking part in Stack Overflow, I have been frustrated to find that sometimes, there is so much more to write than just a question, an answer or an occasional witty comment (or perhaps, more correctly, occasionally witty). Things the likes of Jon Skeet, Eric Lippert, Jeff Atwood and many other Stack Overflow participants blog about all the time, things born from hours and days spent making mistakes solving problems at home or at work, things NSFF (Not Suitable For Facebook – I like my friends just enough not to geek out in front of them like that).

But wait, there's more1

Not only did I have blog envy, but this year I started attending the Ann Arbor .NET Developers group meetings. Through AADND, not only have I met some fascinating people, but I have also learned about some fascinating things. From the .NET Micro Framework to the Windows Workflow Foundation (did you know version 4 was a complete rewrite? me neither), my mind was awash with ideas, projects and procrastination and while I tinkered with and tweeted about these things, deep down, I harboured a desire to do more and to say more. I could not contain it any longer, so here we are.

I intend to blog about anything and everything from my songwriting and recording to my DIY disasters improvisations, but mostly, I expect I will blog about programming. I hope that I'll provide some useful insight or perhaps just useful instruction so others don't have to repeat my mistakes, but most importantly, I hope that I'll learn a few things along the way.

So far in life I've been a software engineer, a strawberry picker, an ostrich farmer, a barman, a sarcastic git, a singer, a runner, a cook, an ex-pat and a gamer (sometimes several at once). I'm often amazed at the things I don't know and I'm always somewhat abstract. I saw friends do it, I saw professionals do it and I wondered, "Why don't I do it?" So I did.

Thanks for stopping by.

1it would be a short blog if there weren't