What does "DoEvents" do in vb6?
Asked Answered
H

4

23

What does "DoEvents" do in vb6 ? Why do I get the error message "Out of stack space" ? What does it mean ?

Hypophyge answered 24/12, 2010 at 13:45 Comment(0)
S
21

DoEvents() allows other Windows messages to be processed.

The reason you get an out of stack space error is probably because DoEvents() is allowing events to occur that call your code again, which again calls DoEvents(), and so on until the stack space, which tracks the return addresses for all these calls, has run out.

In general, I do not recommend using DoEvents() due to problems like these and the fact that it violates the overall event-driven design of Windows.

Salzburg answered 24/12, 2010 at 13:49 Comment(5)
+1 although now that we have seen the latest thinking on asynchronous programming, the Asynchronous CTP, perhaps DoEvents actually looks quite modern?Cyrus
DoEvents() is actually an excellent way to de-prioritize a long running process. And allow the GUI to function. Use them freely but be careful of buttons and other user functions that can trigger something twice. It's pretty much the only way to give the user a cancel button on a long running process.Dielectric
@TrevorD: It is NOT a good way to do that. Windows does this already on its own and you should cooperate with that model. Use a timer or other event to continue processing. DoEvents can cause a lot of problems and it is often used by people who don't understand all the ramifications.Salzburg
Windows does not do that on its own. If you loop in an event, your program will freeze until it completes. Worse still, if your user tries to even move the window, Windows will report the program has stopped responding and ask to force close it. Are you suggesting using a 1ms timer and module level variables to create a loop that behaves in a low priority way?Dielectric
There are workarounds that cooperate with Windows' design. If you can't start a new thread (the proper way to do it) you could use a timer. Also, you mentioned before about needing a window to redraw, you can use UpdateWindow. I know due to VB's limitations it can become more trouble than it's worth. And because of that, there may be Edge cases where you go ahead and use it. But it's just problematic and is a bad programming construct.Salzburg
B
7

A slightly different way of looking at DoEvents is that it flushes the events in the event queue. If your sub or function triggers an event, that event handler becomes a sub that is in line to run as soon as your sub/function is finished. DoEvents says to run that event handler sub now, instead of waiting till the end of your sub.

While I agree in spirit with Jonathon about not using DoEvents, I would temper his statement by saying I only recommend using it if you know exactly why, and know all of the repercussions of changing the order of the event queue this way. Most often, DoEvents is indicated when you want to update your screen in some way from within the context of a subroutine, before the subroutine is finished executing.

An example of this is when you are using the ProgressBar control. Suppose you are iterating through several thousand records, and want to provide feedback to the user as to how far along you are by updating a progress bar. You might interrupt your loop every hundred records and change the value on the progressbar control. However (unless you do something about it) you won't see the change on the screen until after the progressbar's change event handler runs, and that handler won't run until your sub is done executing. It will just get put in the event queue. The way to force the change event to run immediately, suspending your sub, is to call DoEvents. This will flush all existing events from the queue--in this case your progressbar's change event--and will update the progressbar control on the screen.

Now, "out of stack space" basically means that you've been caught in an endless loop of function calls. The most basic way to cause that is this:

Public sub MySub()
    MySub
End Sub

And then call MySub from somewhere. You'll get an out of stack space error. If you look at the Call Stack, you'll see a very long line of calls to MySub.

A well-known real-world example of this would happen in older versions of VB:

Public Sub TextBoxArray_LostFocus(index as Integer)
    If TextBoxArray(index) = "" Then
        TextBoxArray(index).SetFocus
        MsgBox "Please enter a value"
    End If
End Sub

This situation assumes two members of a TextBox control array called TextBoxArray. Now, if the user starts with the first one (index 0) and moves to the second one (index 1) then index 0's LostFocus event will fire. However, VB would also internally set the focus to the index 1 box. Then the code would set the focus back to index 0, firing index 1's LostFocus event! You're caught in a loop. They fixed that in VB5 or 6 by waiting to set the focus until the LostFocus event was done executing.

Biltong answered 31/5, 2013 at 15:18 Comment(6)
Is there an API call which allows any process to do this? I always thought it made Excel specifically process it's events/messageQueue, I'm surprised it can do anything with the message queue of other applications?Pringle
@Pringle Not sure what you mean by other applications exactly, but I might. I believe you are thinking of VBA, which is tied to various MS Office applications: Excel, Word and Access mainly. VB6 is what VBA derived from, and was a standalone programming language, not tied to any of the Office apps. VB6 is now defunct, replaced with VB.Net, which is as different from VB6 as it is from VBA. In VB6, DoEvents flushes the event queue of the application you are writing in VB6.Biltong
i know of VB6, is quite commonly used still too. The strange thing about DoEvents is it waits for Application.SendKeys() to finish but it won't wait for e.g. keybd_event() to finish (stdWindow uses keybd_event under the hood). I have no clue how DoEvents is waiting for these events to be processed, as that's something I deamed not easily doable via WinAPI. Just wondered whether you knew how this worked and/or if there is a set of API calls which do the same as DoEvents does?Pringle
@Pringle You might want to look into CBT Hooking. It was intended to do computer-based training, but it is essentially a way of hooking into pretty much anything and firing some code when something happens. I have some practice code I did maybe 15 years ago that uses CBT hooking to alter the dialog box exposed by the MsgBox function. To find it, go to robertrodes.com/work-samples, select Code, scroll down to Using CBT Hooking to Change the VB MsgBox Function and expand it. HTHBiltong
so you suggest using a KeyboardHook I guess? That still doesn't explain why DoEvents won't wait for keybd_event(), given keybd_event does create WM_KEYUP and WM_KEYDOWN events..., Though it could be that these just aren't tracked by the VBX Application perhaps... Or weren't added to the key event queue in the first place?Pringle
@Pringle We're getting to the limit of my knowledge of this subject. :) But yes, maybe use WH_KEYBOARD instead of WH_CBT with SetWindowsHookEx for what you want to do. The CBT one, when I look it up, is more for window manipulation. Here's the Microsoft doc on the different hooks available (I think you've already found this, but just in case).Biltong
Q
5

I would clarify Johnathon's answer in that it pumps that VB message loop and allows the VB Runtime to process windows messages, which is the opposite of Sleep which allows for Windows to process its events (not necessary in the world of Multicore CPUs and true multitasking OS's but when VB6 was written Windows 9x was the dominant OS and a hard loop that only had DoEvents in it would spike the CPU usage to 100%). So seeing things like

While fDoneFile = False
    DoEvents
    Sleep 55
Wend

was a common pattern throughout the VB6 world.

Quarto answered 27/12, 2010 at 17:36 Comment(0)
M
4

As stated else where, DoEvents allows other events in your application to fire. Here's an example of how you can use DoEvents without the "Out of stack space" issue. This makes sure you don't run through the code multiple times by using a Boolean to indicate the code is running.

Sub Example()
    'Create static variable to indicate the sub is running.
    Static isRunning As Boolean
    'Exit the sub if isRunning
    If isRunning Then Exit Sub
    'Indicate sub is running
    isRunning = True
    'Sub does stuff
    DoEvents
    'Ends up calling sub again
    Example 'Added just to prove via testing.
    'Indicate sub is no longer runningrunning
    isRunning = False
End Sub
Muscadine answered 10/10, 2012 at 1:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.