The Perils of Application.DoEvents

Introduction

Every developer has their pet hates when it comes to code. Whether its int versus System.Int32 or single points of return in a method versus multiple, there’s no shortage of programming patterns or styles to get upset about. One statement that repulses me in particular is Application.DoEvents. It’s such an innocent looking API call. Programming beginners tend to read it as:

Redraw my form please.

I read it as:

Turn my program flow inside out, and potentially introduce a myriad of difficult to reproduce bugs.

Discouraging the use of Application.DoEvents may seem elitist to some. After all, that’s how many VB6ers kept their forms responsive – what’s the issue in .NET?

I’m not about to tell people to stop using it altogether. It’s true that you can use Application.DoEvents without introducing any bugs, but it’s a deceptively simple call which can result in some very complex behaviour. Anytime I see it, I stop to think very carefully about how it’s being used. It gets used an awful lot without consideration to what it’s actually doing.

So What Does it Do?

Windows Forms applications seem a little magical at first. You drag a button to a form, double click it, stick some code in an event handler and you’re done. Your event handlers get called by magic. What else is there to worry about?

At that level not much. However, those event handler calls do come from somewhere. They originate from a bit of code known as the ‘message loop’. The message loop will typically appear in a stack trace as:

  1. FPushMessageLoop(int dwComponentID, int reason, int pvLoopData)

The job of the UI thread is to run this loop until the application exits. The message loop processes events that come from a queue. Messages get placed in the queue by the Operating System, and also by user code (via Control.Invoke for example). One iteration of the loop simply takes a message from the event queue, determines what the message means, and then goes and executes the message’s instruction.

The last call in a default Windows Forms project Program.Main method is a call to Application.Run(Form). The thread calling that method eventually gets to the message loop. You can verify this using Reflector if so inclined.

So your UI thread is sat in the message loop. Windows decides that your form should redraw, so Windows puts a message in the form’s message queue instructing it to repaint. A user clicks one of your form’s buttons, and Windows puts a message in the form’s message queue instructing it to invoke the relevant event handler.

So your Forms application is basically just sat in a big loop somewhere processing messages as they come in. No magic.

Application.DoEvents basically runs the message loop until it’s empty.

Example

To illustrate this, consider a form (assume that it takes a decent amount of time to count to 10000 – say 10 seconds):

  1. public class MyForm : Form
  2. {
  3.     // button1, label1 declarations and initializations; you know the score…
  4.  
  5.     void button1_Click(object sender, EventArgs e)
  6.     {
  7.         for (int i = 0; i < 10000; i++)
  8.         {
  9.             // Update the label's text.
  10.             label1.Text = String.Format("i = {0}", i);
  11.  
  12.             // There should be a message for a repaint sat on the event queue.
  13.             // Let's process it now so that the user can see the label update…
  14.             Application.DoEvents();
  15.         }
  16.     }
  17. }

Imagine starting the form. The thread is sat in the message loop, perhaps doing a repaint every so often. The user clicks the button, and the event handler gets executed. We update the label, and then get to our favourite API call. We recurse back into the message loop. The (simplified) call stack looks a little like

  1. FPushMessageLoop
  2. button1_Click
  3. FPushMessageLoop

A little weird, but perfectly harmless right? Not necessarily. We intended the call to Application.DoEvents to just repaint our form. However, we may have got a little more than we bargained for. Application.DoEvents doesn’t just repaint the window – it processes all messages currently on the event queue. If the user clicked the button again due to a muscular spasm, or because they just double click everything to be super sure they clicked something, we’re going to execute the event handler again…

  1. FPushMessageLoop
  2. button1_Click
  3. FPushMessageLoop
  4. button1_Click

Watch in amazement as your label resets to 0 and starts counting. It seems to make sense, until it finally gets to 9999.

  1. FPushMessageLoop
  2. button1_Click (i = 700)
  3. FPushMessageLoop
  4. button1_Click (i = 9999)

At this point, our second handler invocation is complete. Assuming the message queue is empty, we pop back to the first handler, which then sets the label to the text ‘i = 701’. Cool. We just got from 0 to 700 to 0 to 9999 to 701 and are now progressing again to 9999.

Do you see why I describe it as ‘turning a program inside out’? What should be two calls executing one after another becomes one call executing inside the other. Do you see the potential for state corruption? We could be interacting with a complex object, calling DoEvents, and by the time that’s returned, all bets are off. We don’t know what code DoEvents has executed and whether or not it’s touched the object we’re interacting with.

Does this induce thoughts of threading problems?

Final Thoughts

Of course, you can simply disable button1 when you first enter the handler, and that would seem fine for a small application such as our example. But perhaps my main problem with Application.DoEvents is it’s hidden complexity. It’s appears to be a silver bullet:

Why worry about introducing scary threading – just stick in a call to Application.DoEvents and it’ll work.

It’s not quite as simple as that.

What I hope this article has shown is that even though you’re still only using a single thread, you still have to consider potential threading-like behaviour. Perhaps even worse; because you’re on a single thread you’re no longer protected by locking, unless you’re disallowing recursive lock acquires, in which case at least you’ll get an exception.

When a beginner writes a long running method which periodically updates the UI, they find that their display isn’t updating in the way they expect. They only get to see the final frame. Their next port of call is usually the form’s intellisense member list. They try Refresh and Invalidate which don’t appear to work. Finally, they find Application.DoEvents and everything seems OK. They take this with them for future projects, not realising the complexity they’ve introduced.

I guess my message is this: understand what Application.DoEvents does. Do you really want to execute everything sat on the form’s message queue? If so, and you can make certain guarantees about what’s on that queue, then Application.DoEvents is fine. If you want to update your form whilst a long running operation executes, consider a design involving asynchrony or threading. Either way, think hard about the complexities you’re introducing and try to make your code as simple and as correct as possible. There are no silver bullets.

Share and Enjoy:
  • Print
  • Digg
  • StumbleUpon
  • del.icio.us
  • Facebook
  • Yahoo! Buzz
  • Twitter
  • Google Bookmarks
  • email
  • LinkedIn
  • Technorati