The following code is an example of a message loop that will result in a hang. Library or COM developers may be tempted to use this type of loop to pump messages while waiting for a particular message to be posted (i.e., waiting for background activity to complete), but the wrong approach is used:
// Bad message loop – do not use!
while (GetState() != dwDesiredState)
DWORD dwWakeReason = MsgWaitForMultipleObjects(
0, 0, FALSE, 5000, QS_POSTMESSAGE);
if (WAIT_OBJECT_0 == dwWakeReason)
while (PeekMessage(&msg, hwnd, 0, 0, PM_REMOVE) == TRUE)
if (WM_QUIT == msg.message)
This message loop is only pulling some of the messages off of the message loop. User input is being ignored, and a hang will result! The reason is the dwWakeMask parameter to MsgWaitForMultipleObjects – it is only looking for messages posted to the message queue.
The really bad news is that this message loop will periodically process a message from the thread’s message queue, thereby bypassing the Windows mechanism for detecting stale message queues. Users will see an unresponsive window, but they will not get the ghosting/frosting experience. This is among the worst user experiences possible for all classes of hangs because the user has no recourse through the application’s user interface. Even though ghosting/frosting is not a pleasant experience, at least the OS will allow the user some degree of control over their application.
If the call to PeekMessage() includes a flag for PM_NOYIELD, the application will then experience a hang that results in ghosting/frosting. Of course, the problem of the hang still exists, but at least the user can choose to stop waiting on the application.
The solution is to use a less restrictive wake mask in the call to MsgWaitForMultipleObjects, such as QS_ALLINPUT. Even though this particular piece of code may only be interested in a small class of events to awaken a thread, it needs to be well behaved with all other code that resides in the process.