Daily Archives: April 10, 2005

You are browsing the site archives by date.

Updating Controls From Worker Threads

I thought this was an issue that has been hashed over enough times, but the other day I was asked this question and I’m still amazed that many folks are unfamiliar with this concept.

In WinForms applications, sometimes its desirable to execute a long running method in a worker thread. For instance, you may be retreiving data over a web service that has a heavy load and usually takes several seconds to execute.. When the work has completed, it’s typical that you would want to update the user interface: be that a grid, a tree view control, or some other container that displays the results of your call to the long running process.  Sounds simple enough right?

Before you get in a hurry, consider one thing.  Data created on one thread is owned by that thread as long as it is local data.  This can be overcome though by marshaling the data across threads.  This is a function of Thread Local Storage implemented through managed code. Virtual address space of a process is shared across all threads.  While the data of a thread is unique, it can be “shared” or copied but you need to tell the application to copy the data to a the UI thread before updating the UI with the data.  This can be done using Control.Invoke().

Take a look at what happens when you don’t use this method.  Createa  new Windows Forms project, add a TreeView control (tvResults) and two buttons.( btnNormalExe and btnThreadExe ).  Add the following methods on the form:

public void WorkerThread() {
    Cursor.Current = Cursors.WaitCursor;
    // Simulate Long Process (5 seconds)
    Thread.Sleep( new TimeSpan( 0,0,0,5,0 ) );

    UpdateResults();
    Cursor.Current = Cursors.Arrow;
}

public void UpdateResults() {
  // Update UI
  tvResults.Nodes.Add(
    String.Format( "UI Updated on Thread ID {0} at {1}", 
       AppDomain.GetCurrentThreadId().ToString(),
       DateTime.Now.ToString() ) );
}

For btnNormalExe add the following code:

 

private void btnNormalExe_Click(object sender, System.EventArgs e) {
  WorkerThread();
}

For btnThreadExe add the following code:

private void btnThreadExe_Click(object sender, System.EventArgs e) {
  Thread t = new Thread( new ThreadStart( WorkerThread) );
  t.Start();
}

Now run the form and click on the normal execution button first. Once the method completes, click on the thread execution button.  You’ll receive an exception when the threaded execution completes:

An unhandled exception of type 'System.InvalidOperationException' 
occurred in system.windows.forms.dll
Additional information: The action being performed on this control 
is being called from the wrong thread. You must marshal to the 
correct thread using Control.Invoke or Control.BeginInvoke to 
perform this action.

Just as the exception says, we are going to modify our call to make sure we get back on the UI’s thread before updating it.  In this way, the form’s thread owns the data, not the worker thread. Modify the UpdateResults() method as follows:

public void WorkerThread() {
  Cursor.Current = Cursors.WaitCursor;
  // Simulate Long Process (5 seconds)
  Thread.Sleep( new TimeSpan( 0,0,0,5,0 ) ); 

  //UpdateResults(); 
  this.Invoke( new TreeViewUpdater( UpdateResults ) ); 
  Cursor.Current = Cursors.Arrow;
}

Notice that when you execute both the normal execution button and the threaded execution button the tree view control updates with the same thread ID. This is because the call to control.Invoke puts the call back on the form’s thread.

I will cover this in more depth in the weeks to come, to shed more light on what’s happening in the background.

OOP and Security

A new article has been posted at http://www.codemilitia.com/blogs/tobin.titus/articles/10.aspx

(begin excerpt)

A brief discussion broke out at work today surrounding collections and how they should be exposed in a client class. The proper OO way to expose a collection as a property is to provide a get accessor only. The get accessor should most likely always return an instance. Here is an example:

public class GoodClient {
   private TrustedCollection _data = null;
   public GoodClient() {}
   public TrustedCollection Data {
      get {
         if( _data == null ) {
            _data = new TrustedCollection();
         }
         return _data;
      }
   }
}

The get accessor checks for a null private member and creates a new instance if needed. This follows good encapsulation design.

So what does this have to do with security?

Lets consider that your collection’s “Add” and “Insert” methods might do some data validation on the parameters passed to the method before adding an item to the collection. Let’s look at an example of a class that does some validation:

public class TrustedCollection : CollectionBase {
   public TrustedCollection() { }
   public virtual void Add( DateTime date ) {
      DateTime now = DateTime.Now;
      DateTime is21 = new DateTime( now.Year - 21, now.Month, now.Day );
      if( date > is21 ) {
         throw new ArgumentException(
            "The person must be 21 years old.", "date" );
      }
      List.Add( date );
   }
}

Remember, the first rule of application security is that all input is evil. If your client depends on this validation to occur, it had better not provide a set accessor. Let’s look at how we can defeat this by using an inherited class and a set accessor.

(end of excerpt)

Continue