Andy's observations as he continues to attempt to know all that is .NET...

Thursday, July 06, 2006

Custom Controls and Cross Threading

In .NET 2.0 a feature was introduced so as to raise an exception if an application attempted to access a Windows Control on a thread other than the thread in which the control was initially rendered. This is because Windows Controls are deemed not thread safe, and only expect to interact with a single thread, and thus have no state lock protection. In 1.1 you could blissfully ignore this and most of the time your code would work..

The feature in 2.0 is disabled by default, but when you run under the debugger it is enabled. The debugging environment sets the static member on the Control class CheckForIllegalCrossThreadCalls to true.

Whenever you attempt to get hold of the underlying Windows handle for the control this static member is first inspected and if it evaluates to true the InvokeRequired method is invoked to see if you are indeed on the correct thread. If you are not on the correct thread the exception is thrown

The code below was generated by Reflector, taken from the base Control class

public IntPtr get_Handle()

{

if ((Control.checkForIllegalCrossThreadCalls && !Control.inCrossThreadSafeCall) && this.InvokeRequired)

{

throw new InvalidOperationException(SR.GetString("IllegalCrossThreadCall",

new object[] { this.Name }));

}

if (!this.IsHandleCreated)

{

this.CreateHandle();

}

return this.HandleInternal;

}


There is a slight extra piece of complication in that it is possible to have some control operations that are thread safe, these calls use a class called Control.MultithreadSafeCalllScope which implements IDisposable as a way to wrapup blocks of code that are in fact thread safe and still need to have access to the underlying windows handle.

So using Reflectors Analyze feature I decided to see where this class was used in the framework so that you can see what Windows control methods are Thread safe…

The results were as follows

System.Windows.Forms.Control.BeginInvoke(Delegate, Object[]) : IAsyncResult

System.Windows.Forms.Control.CreateGraphics() : Graphics

System.Windows.Forms.Control.EndInvoke(IAsyncResult) : Object

System.Windows.Forms.Control.get_InvokeRequired() : Boolean

System.Windows.Forms.Control.get_WindowText() : String

System.Windows.Forms.Control.Invalidate(Boolean) : Void

System.Windows.Forms.Control.Invalidate(Rectangle, Boolean) : Void

System.Windows.Forms.Control.Invalidate(Region, Boolean) : Void

System.Windows.Forms.Control.Invoke(Delegate, Object[]) : Object

A few posts back I saw that Invalidate was possible, but now I can see that I can also get the text associated with a windows control.

Anyway back to the plot….the reason for all this was to make my own custom controls behave the same as the underlying windows controls. I basically wanted to ensure that my control was only ever interacted with on a single thread. Since with UI apps you generally expect everything to happen on the main UI thread you don’t want to put the overhead of locking etc in as the majority of cases everything is happening on the UI thread.

So I’ve added a new method to my control called CheckForIllegalCrossCall I make a call to this method inside each of my properties..

private void CheckForIllegalCrossCall()

{

if ((Control.CheckForIllegalCrossThreadCalls) && (InvokeRequired))

{

throw new InvalidOperationException("Illegal cross Thread operation");

}

}

Now I don’t need to worry about state corruption as state can now only be manipulated on a single thread. When debugging we will make the call to InvokeRequired and when not we will simply assume that the programmer knows what he’s doing…

2 comments:

Anonymous said...

thanks man

Eralp,from Turkey

www.eralperat.com

Anonymous said...

Do you know the warhammer gold, In here you can buy the warhammer Online gold, Do you know that the warhammer money in the game is very important, If you had more cheap warhammer Online gold . I think you can get the tall level, quickly come here to buy warhammer gold.

About Me

My photo
Im a freelance consultant for .NET based technology. My last real job, was at Cisco System were I was a lead architect for Cisco's identity solutions. I arrived at Cisco via aquisition and prior to that worked in small startups. The startup culture is what appeals to me, and thats why I finally left Cisco after seven years.....I now filll my time through a combination of consultancy and teaching for Developmentor...and working on insane startups that nobody with an ounce of sense would look twice at...