Debugging UI

I’ve talked before about System.Threading.SynchronizationContext, as well as BeginInvoke/InvokedRequired/IsHandleCreated. In a multi-threaded Windows Forms application they can easily be mis-used, introducing difficult to find bugs.

One such not-so-subtle bug (application hang) is particularly nasty, and is described fairly well here. Distilled down, the application hangs, usually when the computer comes out of sleep mode, unlocks, or another similar event occurs. The hang happens in the firing of an event handler, called from “SystemEvents.OnUserPreferenceChanged”. The cause is that OnUserPreferenceChanged is trying to be super nice, and invoke the event in the appropriate context for each event subscriber. That means that if a Control subscribes to the event, the handler will be called on the UI thread. If user code (on a background thread) subscribes, the handler will be called on an arbitrary thread, etc. The problem occurs when, at some point in the past, a Form or Control was created on a background thread. The creation of the control “installed” a WindowsFormsSynchronizationContext as the current SynchronizationContext (this is default behavior). When OnUserPreferenceChanged attempts to Send (Invoke) to the appropriate thread context, it hangs because the WindowsFormsSynchronizationContext is on the wrong thread, and thus has no message pump with which to process messages.

Blah blah blah. Right now you’re thinking “Whatever dude, I’m a web developer, man, I’m just trying to fix this bug in that other jerk’s code.” Fair enough, you can read the linked post for more gory details. What you care about is the hard part. Well, hard for web developers anyway. ;) By the time the app hangs, it’s too late to find out where the problem occurred. In a medium-to-large application, how do you find the control that was created on the wrong thread? Here’s how to do it in a matter of seconds.

1. Name your UI thread. If you’re not already doing this, it’s a good idea in general. I like to call mine “UI”, personally. In your “static void Main()” add this single line of code:

Thread.CurrentThread.Name = "UI";

2. Set a breakpoint deep in the bowels of the BCL. What we want to do is cause our application to break when a WindowsFormsSynchronizationContext gets assigned to the current thread. I cracked open Reflector to look at the constructor for WindowsFormsSynchronizationContext:

And I see that there’s a call to “Application.ThreadContext.FromCurrent()”. Close enough for government work, it’ll do for what I want. I didn’t feel like figuring out how to specify a constructor call when setting a breakpoint. (If you know how, leave a comment so we can all learn!) Add a breakpoint to that method call. In Visual Studio, go to the “Debug” > “New Breakpoint” > “Break at Function…” menu. In the “Function” area type the full path to the function: System.Windows.Forms.Application.ThreadContext.FromCurrent()

When you hit “OK” you’ll get a warning about IntelliSense not finding the specified location. Hit “Yes” to set the breakpoint anyway.

3. Run your application. Depending on what version of Visual Studio you’re running, and whether you’ve got source-level debugging for the Framework turned on, you’ll either get the breakpoint on some code, or you’ll get this message box:

It doesn’t matter, you can press “OK” or “Show Disassembly”, whatever floats your boat. Go to the “Call Stack” debugging window, right-click on the red breakpoint circle located at the top of the stack frame, and select “Breakpoint” > “Filter…”

4. Add a filter to this breakpoint so that it only breaks when the current threads’ name isn’t “UI”.

After you hit “OK”, continue running your application in the debugger. The first time a WindowsFormsSynchronizationContext is created and it’s not on the UI thread, BAM. There’s your problem, and there’s your stack trace allowing you to find the bad code.

This worked like a champ for me today, hopefully you have as much success with it too.

This entry was posted on Monday, December 15th, 2008 at 2:01 pm and is filed under .net, debugging, windows forms. You can follow any responses to this entry through the RSS 2.0 feed. Both comments and pings are currently closed.

7 Responses to “Debugging UI”

  1. Fredrik Says:

    Very clever. Thanks for the tip!

  2. Dew Drop - December 16, 2008 | Alvin Ashcraft's Morning Dew Says:

    [...] Debugging UI (Aaron Lerch) [...]

  3. Steven King Says:

    To break on Constructor:
    System.Windows.Forms.WindowsFormsSynchronizationContext..ctor()

    The name of the Constructor is .ctor, so there needs to be two periods in the call. You can see the name of the constructor method by examining the il codes from ildasm.

  4. Abhimanyu Says:

    Hi!
    The issue for us seems to be only in production. in dev, i.e. with studio and all, the bug never occurs.
    What i was looking for is a way to log the control cal stack which is ‘created’ in non ui thread in runtime in production.

    Is there a way i can add some diagnostic code to log when the phenomenon occurs at runtime w/o debugger?

    I’m not even sure what tp type in google to get what i want :(

  5. Gary Says:

    Don’t forget to disable the ‘Just My code’ option in Tools->options->debugging.

    This worked a treat, led me straight to the problem, I had already spent a man week trying to find it, with this it took an hour!

  6. Rohit Says:

    Thanks a lot man, this debug info really helps and thanks to gary as well for mentioning Visual Studio debug option.

    I was creating form instance on backGround worker thread with startup wizard dispaly before loading actual application MDI form. though I was disposing form object and setting to null but didn’t help.

    It never use to hang with Visual studio debug evn, but outside studio (non dev env) always does.

  7. Jeff Moser Says:

    Great post! You can also break on just:

    WindowsFormsSynchronizationContext()