Welcome to MSDN Blogs Sign in | Join | Help

Pumping messages while waiting for a period of time

We can use the MsgWaitForMultipleObjects function (or its superset MsgWaitForMultipleObjectsEx) to carry out a non-polling "sleep while processing messages".

#define MSGF_SLEEPMSG 0x5300

BOOL SleepMsg(DWORD dwTimeout)
{
 DWORD dwStart = GetTickCount();
 DWORD dwElapsed;
 while ((dwElapsed = GetTickCount() - dwStart) < dwTimeout) {
  DWORD dwStatus = MsgWaitForMultipleObjectsEx(0, NULL,
                    dwTimeout - dwElapsed, QS_ALLINPUT,
                    MWFMO_WAITANY | MWMO_INPUTAVAILABLE);
  if (dwStatus == WAIT_OBJECT_0) {
   MSG msg;
   while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
    if (msg.message == WM_QUIT) {
     PostQuitMessage((int)msg.wParam);
     return FALSE; // abandoned due to WM_QUIT
    }
    if (!CallMsgFilter(&msg, MSGF_SLEEPMSG)) {
     TranslateMessage(&msg);
     DispatchMessage(&msg);
    }
   }
  }
 }
 return TRUE; // timed out
}

This function pumps messages for up to dwTimeout milliseconds. The kernel of the idea is merely to use the MsgWaitForMultipleObjects/Ex function as a surrogate for WaitMessageTimeout, pumping messages until the cumulative timeout has been reached. There are a lot of small details to pay heed to, however. I've linked them to earlier postings that discuss the specific issues, if you need a refresher. The CallMsgFilter you might find gratuitous, but you'll change your mind when you realize that users might press a keyboard accelerator while you're sleeping, and you presumably want it to go through somebody's TranslateAccelerator. The message filter lets you hook into the modal loop and do your accelerator translation.

Extending this function to "wait on a set of handles up to a specified amount of time, while pumping messages" is left as an exercise. (You can do it without changing very many lines of code.)

[Call the right function. -2pm]

Published Thursday, January 26, 2006 7:00 AM by oldnewthing
Filed under:

Comments

# re: Pumping messages while waiting for a period of time

Thursday, January 26, 2006 10:39 AM by asdf
MWFMO_WAITANY?

# re: Pumping messages while waiting for a period of time

Thursday, January 26, 2006 10:41 AM by BryanK
Well, I must say that the code we wrote to do this seems a lot simpler in VB6 (though I don't know if it's entirely correct!):

Do Until <button was clicked>
If MsgWaitForMultipleObjects(...) = WAIT_OBJECT_0 Then
DoEvents
EndIf
Loop

(This used to be a "Do Until <button was clicked> DoEvents" loop, which used a ton of CPU doing exactly nothing. The DoEvents call runs VB6's message loop on any messages that have come in, but if nothing's there, it just returns immediately.)

I don't know if this is actually 100% correct because I don't know whether DoEvents makes the CallMessageFilter call, or handles WM_QUIT properly. I would guess that WM_QUIT would be OK, but I don't know for sure.

(We also didn't have a timeout, we were just waiting for the user to click a button. We needed to keep pumping messages because we had several VB Timer objects set up to pacify a hardware watchdog, so if we never called DoEvents, the timer handlers would never fire, and the watchdog would reset the machine.)

# re: Pumping messages while waiting for a period of time

Thursday, January 26, 2006 10:51 AM by oldnewthing
Determining the correct value for MWFMO_WAITALL I leave an an exercise to see if you really understand the issue.

BryanK: If you aren't interested in timeout behavior, then you can just use WaitMessage.

# re: Pumping messages while waiting for a period of time

Thursday, January 26, 2006 11:15 AM by Mike Dimmick
OK, what you're doing with MWMO_INPUTAVAILABLE is now completely contrary to MSDN. Is the MSDN documentation incorrect? Should you be using MsgWaitForMultipleObjectsEx instead?

The value of MWFMO_WAITALL should be TRUE while the value of MWFMO_WAITANY should be FALSE, if the MSDN documentation for MsgWaitForMultipleObjects is correct.

# re: Pumping messages while waiting for a period of time

Thursday, January 26, 2006 11:53 AM by Carlos
MsgWaitForMultipleObjects is a wrapper around MsgWaitForMultipleObjectsEx and from the code it looks like the documentation is correct:

xor eax,eax
cmp dword ptr [ebp+10h],eax
setne al
push eax

i.e. the third parameter is treated as a BOOL and 0 or 1 (MWMO_WAITALL) is sent to MsgWaitForMultipleObjectsEx. So the flags Raymond passes are getting eaten.

# re: Pumping messages while waiting for a period of time

Thursday, January 26, 2006 4:35 PM by oldnewthing
I called the wrong function, but if you have been reading and understanding you should have been able to fix it yourself.

# re: Pumping messages while waiting for a period of time

Thursday, January 26, 2006 5:02 PM by BryanK
I went back and looked at the code -- it wasn't using a timeout, but it was waiting for an event to get signaled.

Basically, we were trying to emulate the VB6 MsgBox function (which doesn't return until the message-box goes away), while still allowing the watchdog timers to fire (so we had to pump messages somehow). Normally the VB MsgBox function disables timers.

We brought up a form using vbModeless, and the form had an auto-reset event that it signaled inside its button click handler.

Then the replacement MsgBox function did the MsgWaitForMultipleObjects(...) loop, with the auto-reset event handle from the form that it brought up.

I'm still not sure this is safe, but now it's more because I don't know whether the auto-reset event was the right kernel object to wait on (since VB6 is single-threaded, and the signalling thread *was* the thread that later re-waited). I believe it was probably OK, because it does work (we didn't use PulseEvent, but rather SetEvent).

# re: Pumping messages while waiting for a period of time

Thursday, January 26, 2006 5:42 PM by Joshua Schaeffer
I would have preferred a way to get an event handle for the message queue, if only because a custom function like MsgWaitForMultipleObjects is difficult (if not impossible) to integrate with existing C++ general framework synchronization wrappers.

# re: Pumping messages while waiting for a period of time

Friday, January 27, 2006 2:30 AM by Anthony Wieser
Looks like the flags and timeout are swapped in:
MsgWaitForMultipleObjectsEx(0, NULL, WFMO_WAITANY | MWMO_INPUTAVAILABLE, dwTimeout - dwElapsed, QS_ALLINPUT);

Shouldn't it be:
MsgWaitForMultipleObjectsEx(0, NULL, dwTimeout - dwElapsed, WFMO_WAITANY | MWMO_INPUTAVAILABLE, QS_ALLINPUT);


And MWFMO_WAITANY is 0, right?

# re: Pumping messages while waiting for a period of time

Saturday, January 28, 2006 10:39 AM by ghbyrkit
By jove, I think that Anthony is correct about there being an error in Raymond's code! And I was about to use this method just as incorrectly as Raymond did! Why did it take all of us so long to spot this? And I also think that MWFMO_WAITANY isn't defined in the system header files (at least the ones that VS6 uses), and that its value should be zero.

# re: Pumping messages while waiting for a period of time

Saturday, January 28, 2006 10:44 AM by ghbyrkit
And upon further reflection (and consulting the Platform SDK documents), the QS_ALLINPUT goes BEFORE the MWMO_INPUTAVAILABLE parameter as well!

So shouldn't it be:
DWORD dwStatus = MsgWaitForMultipleObjectsEx(
0, // handle count
NULL, // handle array
dwTimeout - dwElapsed, // time to wait
QS_ALLINPUT, // wait options
MWFMO_WAITANY | MWMO_INPUTAVAILABLE // wait options (MWFMO_WAITANY is NOT defined by the system!)
);

# The dangers of sleeping on a UI thread

Friday, February 10, 2006 10:00 AM by The Old New Thing
It clogs up the messaging system.

# The Blog of Justice &raquo; MsgWaitForMultipleObjectsEx

Monday, October 02, 2006 6:36 PM by The Blog of Justice » MsgWaitForMultipleObjectsEx
New Comments to this post are disabled
 
Page view tracker