The dialog manager, part 7: More subtleties in message loops

Date:April 6, 2005 / year-entry #87
Tags:code
Orig Link:https://blogs.msdn.microsoft.com/oldnewthing/20050406-57/?p=35963
Comments:    10
Summary:Last time, we solved the problem with the EndManualModalDialog function by posting a harmless message. Today, we're going to solve the problem in an entirely different way. The idea here is to make sure the modal message loop regains control, even if all that happened were incoming sent messages, so that it can detect that...

Last time, we solved the problem with the EndManualModalDialog function by posting a harmless message. Today, we're going to solve the problem in an entirely different way.

The idea here is to make sure the modal message loop regains control, even if all that happened were incoming sent messages, so that it can detect that the fEnded flag is set and break out of the modal loop.

Instead of changing the EndManualModalDialog function, we will change the modal message loop.

int DoModal(HWND hwnd)
{
 DIALOGSTATE ds = { 0 };
 HWND hdlg = CreateDialogParam(g_hinst, MAKEINTRESOURCE(1),
             hwnd, DlgProc, reinterpret_cast<LPARAM>(&ds));
 if (!hdlg) {
  return -1;
 }

 EnableWindow(hwnd, FALSE);
 MSG msg;
 msg.message = WM_NULL; // anything that isn't WM_QUIT
 while (!ds.fEnded) {
  if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
   if (msg.message == WM_QUIT) { /*  fixed 8am */
    break;
   } else if (!IsDialogMessage(hdlg, &msg)) {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
   } /* fixed 10am */
  } else if (!ds.fEnded) {
   WaitMessage();
  }
 }
 if (msg.message == WM_QUIT) {
  PostQuitMessage((int)msg.wParam);
 }
 EnableWindow(hwnd, TRUE);
 DestroyWindow(hdlg);
 return ds.iResult;
}

We changed the call to GetMessage into a call to the PeekMessage function, asking to remove the peeked message if any. Like GetMessage, this delivers any incoming sent messages, then checks if there are any posted messages in the queue. The difference is that whereas GetMessage keeps waiting if there are no posted message, PeekMessage returns and tells you that there were no posted messages.

That's the control we want. If PeekMessage says that it couldn't find a posted message, we check our fEnded flag once again, in case an incoming sent message set the fEnded flag. If not, then we call the WaitMessage function to wait until there is something to do (either an incoming sent message or a posted message).

Exercise: If the whole point was to regain control after sent messages are delivered, why isn't there a test of the fEnded flag immediately after DispatchMessage returns?


Comments (10)
  1. Jack Mathews says:

    Raymond… Completely off topic, but you may want to make a post about the new login stuff.

    Or if you could at least get a log in link on your page, that might help. It took some digging to figure out that I had to

    a) Log in to post a comment.. and

    b) Go to the main blogs.msdn.com to do it.

    I think the reduction in your comment traffic shows that other people are just as confused.

  2. Dave says:

    "…why isn’t there a test of the fEnded flag immediately after DispatchMessage returns? "

    There is, or at least the only thing between the return of DispatchMessage and the test of fEnded is most likely a single JMP instruction to the top of the while loop.

    Since IsDialogMessage has the side-effect of processing the message when it returns TRUE, the DispatchMessage in the main loop isn’t being used for any case where fEnded might be set.

    Is that loop handling WM_QUIT correctly now? PeekMessage retrieves WM_QUITs but GetMessage doesn’t and returns 0.

  3. waleri says:

    > why isn’t there a test of the fEnded flag immediately after DispatchMessage returns

    Because DispatchMessage() is invoked only for non-dialog messages. If for some reason dialog terminates while processing some of these messages, we’ll miss the check

  4. CoMargo says:

    But why not to use Event and MsgWaitForMultipleObjects instead of using boolean variable?

  5. Adrian says:

    Call me stupid, but I don’t get how this solves the problem. Can’t you get stuck in WaitMessage just like the previous implementation got stuck in GetMessage? Wouldn’t you still need to post a message to get the loop to wake up and see that the flag has changed?

    I suppose that if you assume the EndManualModalDialog only gets called while processing a message that was dispatched from the modal loop, it’ll work, but that seems like you’re solving for a specific case.

    [A sign-in link from the individual blog pages would be nice, since many of us navigate directly to postings we’re interested in from the RSS feed.]

  6. "But why not to use Event and MsgWaitForMultipleObjects instead of using boolean variable?"

    I would ask the question in reverse. Why use an Event and MsgWaitForMultipleObjects when a simple boolean variable is all you need?

    [Alas I do not control the server software. I’ll forward these concerns to the people who run the server.]

  7. Dave says:

    Okay, it wasn’t handling WM_QUIT correctly. To correct my previous comment, I meant to say that GetMessage returns 0 on WM_QUIT, but it does retrieve it.

    One topic I’d like to see discussed is when it is "safe" to ignore the fact that GetMessage can return -1. For example, am I safe if hwnd is valid and &msg is a good address? The GetMessage docs are pretty insistent that -1 is a possible outcome and should be handled gracefully.

  8. Ben Hutchings says:

    That code still isn’t quite right; the braces don’t match. If I’m following this correctly I think there should be two close-braces after the DispatchMessage() call.

  9. asdf says:

    Doesn’t this suffer from the same thing as http://blogs.msdn.com/oldnewthing/archive/2005/02/17/375307.aspx ?

  10. BillArnette says:

    Adrian: As I understand it (from the MSDN docs) GetMessage does not return until a *posted* message is in the queue. WaitMessage returns if *any* message (posted, input, sent, WM_PAINT, WM_TIMER) is in the queue.

    My stab at the exercise: From the SDK PeekMessage docs…

    "The PeekMessage function *dispatches* incoming *sent messages*, checks the thread message queue for a posted message, and retrieves the message (if any exist)."

    Therefore sent messages never get to the DispatchMessage branch.

Comments are closed.


*DISCLAIMER: I DO NOT OWN THIS CONTENT. If you are the owner and would like it removed, please contact me. The content herein is an archived reproduction of entries from Raymond Chen's "Old New Thing" Blog (most recent link is here). It may have slight formatting modifications for consistency and to improve readability.

WHY DID I DUPLICATE THIS CONTENT HERE? Let me first say this site has never had anything to sell and has never shown ads of any kind. I have nothing monetarily to gain by duplicating content here. Because I had made my own local copy of this content throughout the years, for ease of using tools like grep, I decided to put it online after I discovered some of the original content previously and publicly available, had disappeared approximately early to mid 2019. At the same time, I present the content in an easily accessible theme-agnostic way.

The information provided by Raymond's blog is, for all practical purposes, more authoritative on Windows Development than Microsoft's own MSDN documentation and should be considered supplemental reading to that documentation. The wealth of missing details provided by this blog that Microsoft could not or did not document about Windows over the years is vital enough, many would agree an online "backup" of these details is a necessary endeavor. Specifics include:

<-- Back to Old New Thing Archive Index