TabletPC Development Gotchas Part1: Handling exceptions in Winforms Ink events

Over the past few years of developing apps using the TabletPC platform, I came across a couple of gotchas that are not exactly obvious to solve or workaround (at least they were not obvious to me). I figured I should blog about those and the respective solutions or workarounds to save others some head-scratching.

Here is the first one - it applies to the Microsoft.Ink assembly for developing inking apps using Winforms. Consider the following trivial app:

using System;

using System.Drawing;

using System.Windows.Forms;

using Microsoft.Ink;

class Program : Form

{

    private InkOverlay _inkOverlay;

    public Program()

    {

        _inkOverlay = new InkOverlay(this);

        _inkOverlay.Enabled = true;

        _inkOverlay.Stroke += new InkCollectorStrokeEventHandler(_inkOverlay_Stroke);

    }

    void _inkOverlay_Stroke(object sender, InkCollectorStrokeEventArgs e)

    {

        int foo = 2;

        int bar = 5 / (2 - foo);

        e.Stroke.DrawingAttributes.Color = Color.Red;

        this.Invalidate();

    }

    static void Main(string[] args)

    {

       Application.Run(new Program());

    }

}

The goal of the app is that each new stroke turns red once the stroke has been completed. However, when you run the app, this doesn't actually happen. The app works, but the ink remains in the default color (black). If you set a breakpoint on the line that sets the stroke's color to red, the breakpoint never gets hit. If you set a breakpoint at the top of the Stroke handler and step through the code, you will notice that the code execution returns to the app just before the color would be set.

Why is that? Well, in this simple example it's easy to see that there is a bug (division by zero). But why isn't the exception reported to us as an unhandled exception? The answer is not that obvious. It is due to the fact that the Microsoft.Ink assembly is really just a managed wrapper around a COM library (InkObj.dll). Take a look at the callstack when the event handler gets hit and you will see that your handler is being called from native code. So the unhandled, managed DivideByZero exception bubbles up the stack and eventually gets eaten by the native code that sits upstream from your event handler.

Now this behavior can result in some serious headscratching for the application developer. In most real-world cases, the bug is not as easy to spot as the one in my simplistic example. To avoid getting into these kind of issues, I recommend that you put your ink event handling code in try/catch blocks, so you can respond to exceptions thrown by your code, before they get eaten by the native/managed interop. Something along the lines of:

void _inkOverlay_Stroke(object sender, InkCollectorStrokeEventArgs e)

{

    try

    {

        int foo = 2;

        int bar = 5 / (2 - foo);

        e.Stroke.DrawingAttributes.Color = Color.Red;

        this.Invalidate();

    }

    catch (Exception exc)

    {

        MessageBox.Show(exc.ToString());

    }

}

 

With this, the MessageBox will now tell you that there was a DivideByZero exception, and it will tell you where it happened.

Next post in this series: TabletPC Development Gotchas Part2: Deploying InkPicture applications to Windows XP desktops