Whidbey help for multithreading woes: CheckForIllegalCrossThreadCalls

A somewhat common programming error is to directly call back onto a piece of UI when you’re on the wrong thread. 

Consider this seemingly innocuous piece of code:

       System.Timers.Timer t = new System.Timers.Timer();

public Form1() {
InitializeComponent();
t.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);
t.Enabled = true;
}

        void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) {
this.richTextBox1.Text += "Whoops I'm on the wrong thread!";
}

Reading up about the various Timers available in the Framework, this particular timer is the wrong choice for updating Windows Forms UI, as it calls back on a different thread. (Side note: see InvokeRequired, Invoke, BeginInvoke for the correct way to update the RichTextBox from another thread).

Because the RichTextBox is not Thread-Safe, it can sometimes trip over its own EditStreamProc – (putting callstack here as this seems to be a somewhat common problem folks run into in v1).

System.NullReferenceException: Object reference not set to an instance of an object.
at System.Windows.Forms.RichTextBox.EditStreamProc(IntPtr dwCookie, IntPtr buf, Int32 cb, Int32& transferred)
at System.Windows.Forms.UnsafeNativeMethods.CallWindowProc(IntPtr wndProc, IntPtr hWnd, Int32 msg, IntPtr wParam, IntPtr lParam)
at System.Windows.Forms.NativeWindow.DefWndProc(Message& m)
at System.Windows.Forms.Control.DefWndProc(Message& m)
at System.Windows.Forms.Control.WndProc(Message& m)
at System.Windows.Forms.TextBoxBase.WndProc(Message& m)
at System.Windows.Forms.RichTextBox.WndProc(Message& m)
at System.Windows.Forms.ControlNativeWindow.OnMessage(Message& m)
at System.Windows.Forms.ControlNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)

Unfortunately, this callstack is not helpful for folks trying to figure out what exactly happened to their RichTextBox. In Whidbey, we’ve added a new debugger-only feature: a static property on Control called CheckForIllegalCrossThreadCalls. 

Control.CheckForIllegalCrossThreadCalls is defaulted to true when starting a windows forms application under the debugger.

If you run the same application under the debugger in Whidbey, an exception would be thrown the moment the Control.Handle property is accessed from the wrong thread:

Control 'richTextBox1' accessed from a thread other than the thread it was created on.

(note: you’ll need open Debug->Exceptions menu item and check off Common Language Runtime Exceptions when thrown)

Obviously, there’s no great way for Windows Forms to catch 100% the potential problems with multithreading, but I think it’s a great start. If this happens to pose a compatibility problem for your program, you can set Control.CheckForIllegalCrossThreadCalls to false. (You can also do this while debugging in VS by using the Immediate Window and typing in Control.CheckForIllegalCrossThreadCalls = false).