Nov 282009
 

From an end user point of view, your application UI is the single most important part of your application. There may be a million lines of code in the business layer doing all sorts of magic, but try giving a unresponsive UI with it, and no guessing, how many takers you will find for your application. So a lot of thinking goes into how to keep the UI always responding to the user’s actions and conveying that processing is being done.

A very common piece of code which I have observed is the call to the method Application.DoEvents() in some long running iterations to update the UI if there is some long running operation in the background. This could be a control like the ProgressBar or the Form Itself. So even when the UI is moved around or dragged below and above other windows, it always manages to redraw itself. Application.DoEvents is certainly not the only way to do this, nor the best either. Some common alternatives are using a BackgroundWorker, or write your own threading logic. But given the choice of writing threading, its natural that quite a few developers would be inclined to use DoEvents which does the same work with just one line of code.

Here is some sample code for Application.DoEvents

string[] _strarray = Directory.GetDirectories("Path with a lot of files");
          foreach (string _tempString in _strarray)
          {
              string[] _fileArray = Directory.GetFiles(_tempString, "*", SearchOption.AllDirectories);
              foreach (string _fileString in _fileArray)
              {
                  listBox1.Items.Add(_fileString);
                  Application.DoEvents();
              }
          }

This code works great and makes the UI completely responsive irrespective of the background function running. How does this work? Windows apps are event driven. They respond to mouse moves, clicks and various events. Whenever the window is moved, windows detects that the Client area is invalidated so it has to be redrawn. So how do so many windows talk to each other and make sure they process all the events? A Message Queue is where all the messages are queued up so they can be processed one by one. This is how they are processed

   while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0 )
   {
      if (bRet == -1)
      {
         // handle the error and possibly exit
      }
      else
      {
         TranslateMessage( &msg );
         DispatchMessage( &msg );
      }
   }

As you can see Windows continuously looks for Messages in the queue, the moment it finds one, it enters the loop and checks if any error was thrown, if not the message is translated and dispatched to the right window procedure. When one message is being processed, the others like WM_PAINT to redraw your UI, since these messages are waiting for your long operation to complete, the form stops responding and you get the irritating ghost window.

notRespondingh

So What does Application.DoEvents() actually do? It takes the remaining events which are still waiting and processes them. This include the WM_PAINT messages which are waiting to update your UI. So your applications seems much more responsive, all with 2 lines of code. Great, so why use BackgroundWorkers or Threading when Application.DoEvents does your job. Because Application.DoEvents doesnt really limit itself to the WM_PAINT messages, it tries to clear up the whole queue. This can lead to unpredictable results. For e.g. the user might again trigger the long running operation leading to a deadlock for resources. Such errors might be hard to trace out and debug as well.

So something that looks easier in the long run might end up costing you more effort. So where do we use Application.DoEvents()? The method isn’t deprecated yet, so Microsoft doesn’t consider it all that bad, as long as the programmer knows that he is taking a risk. It might be great for prototyping your application for customer demos, where you only care about functionality. But in production code, its best to write out your own threading logic or atleast a BackgroundWorker component, especially in cases where you are accessing resources that have locks, for e.g. editing a file which is already open.