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.