Difference between revisions of "Why DoEvents is Evil"

From HashVB
Jump to: navigation, search
 
 
(7 intermediate revisions by 2 users not shown)
Line 1: Line 1:
It is common in programming to need to have a timer of some form that will freeze your routine for a specified amount of time.  One way to do this is to use the Sleep() API.  Unfortunately there is a downside to this method, and that is that it will freeze your process. Not only does this mean that your APP's GUI updates will not occur until AFTER Sleep() returns, but also that your app will appear frozen to task manager.
+
Alot of code out there relies on the DoEvents statement, or even advertises the use of it.
 +
DoEvents can be useful in some cases, but in general it should not be used !
  
So what's the solution?  Why, create your own Sleep() function of course!
+
A call to DoEvents blocks until all the WM_MESSAGES in the message queue are processed,
 +
WM_MESSAGES are used by Windows (all versions of it) to notify an application of an "event".
 +
(Mouse or keyboard activity, timers, etc...)
  
  Private Declare Function GetTickCount& Lib "kernel32" ()
+
While the DoEvents statement is blocking the normal execution flow of the application, the GUI remains responsive however, this means that another block of code can be entered that also has a DoEvents statement in it, or even that the same block of code can be entered again.
  Private Declare Sub APISleep Alias "Sleep" Lib "kernel32.dll" (ByVal dwMilliseconds As Long)
+
  
   Private Sub Sleep(ByVal dwMilliseconds As Long)
+
There are now two DoEvents calls blocking, if another DoEvents statement is encountered it will block again... this can cause stack overflow easily.
      Dim initTickCount As Long
+
 
     
+
The following code demonstrates this behavior...
      initTickCount = GetTickCount
+
 
      Do Until GetTickCount - initTickCount >= dwMilliseconds
+
   Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
          APISleep 10
+
 
          'Use the API call for sleep to prevent 100% cpu usage
+
        For n As Integer = 1 To 10000
          DoEvents
+
            Button1.Text = n.ToString
      Loop
+
            Application.DoEvents()
 +
        Next
 +
 
 
   End Sub
 
   End Sub
  
This will freeze the routine from which it was called for the specified time, but leave the process free to update the GUI.
+
When you click that button, the loop will be entered, and its progress shown on the button itself.
 +
The UI is responsive, meaning you can click that same button again, the first loop will now be paused on the DoEvents statement, until the second loop terminates.
 +
Click it again to start a thirth loop, and so on...
 +
 
 +
Next to risking stack overflow exceptions, it is almost impossible to predict the flow of your application, making debugging very painful.
 +
 
 +
If you have a process that will take a long time and you have to use DoEvents, you should either disable any User Interface that can "interfere" with the process or set a flag to say it is working. You can then check this flag when they try and start it again or shut the program down. This is a simple Mutex as in it can only be executing once at any time.
 +
 
 +
  Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
 +
     
 +
        Static Overlapped As Boolean = False
 +
 
 +
        If Overlapped = False Then
 +
          Overlapped = True
 +
          For n As Integer = 1 To 10000
 +
              Button1.Text = n.ToString
 +
              Application.DoEvents()
 +
          Next
 +
          Overlapped = False
 +
        End If
 +
 
 +
  End Sub

Latest revision as of 21:57, 13 October 2005

Alot of code out there relies on the DoEvents statement, or even advertises the use of it. DoEvents can be useful in some cases, but in general it should not be used !

A call to DoEvents blocks until all the WM_MESSAGES in the message queue are processed, WM_MESSAGES are used by Windows (all versions of it) to notify an application of an "event". (Mouse or keyboard activity, timers, etc...)

While the DoEvents statement is blocking the normal execution flow of the application, the GUI remains responsive however, this means that another block of code can be entered that also has a DoEvents statement in it, or even that the same block of code can be entered again.

There are now two DoEvents calls blocking, if another DoEvents statement is encountered it will block again... this can cause stack overflow easily.

The following code demonstrates this behavior...

 Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
  
       For n As Integer = 1 To 10000
           Button1.Text = n.ToString
           Application.DoEvents()
       Next
 
 End Sub

When you click that button, the loop will be entered, and its progress shown on the button itself. The UI is responsive, meaning you can click that same button again, the first loop will now be paused on the DoEvents statement, until the second loop terminates. Click it again to start a thirth loop, and so on...

Next to risking stack overflow exceptions, it is almost impossible to predict the flow of your application, making debugging very painful.

If you have a process that will take a long time and you have to use DoEvents, you should either disable any User Interface that can "interfere" with the process or set a flag to say it is working. You can then check this flag when they try and start it again or shut the program down. This is a simple Mutex as in it can only be executing once at any time.

 Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
      
       Static Overlapped As Boolean = False 
 
       If Overlapped = False Then
          Overlapped = True
          For n As Integer = 1 To 10000
              Button1.Text = n.ToString
              Application.DoEvents()
          Next
          Overlapped = False
       End If
 
 End Sub