Why are my posted messages getting lost when the user drags my window around?


This question was inspired by an actual customer question, but I changed a lot of it around to make for a more interesting story. (Trust me, the original story was even more boring.)

A customer's background thread posted a message to the main UI thread to signal something (details not important). They found that the posted message was never received if the user was in the process of dragging the main window around at the time the message was posted. Why would dragging a window cause posted messages to be lost? "We used to post a thread message, but then we saw that thread messages are eaten by modal loops, so we switched to posting a message, as you recommended. But that didn't help."

Dragging a window doesn't cause messages to be lost. The modal loop created by the window dragging code calls Dispatch­Message to deliver the posted message to its target window's window procedure.

"Oh, we don't handle the message in the window procedure. We process it here:

BOOL MyApp::PreTranslateMessage(MSG *pmsg)
{
  if (pmsg->message == OUR_SPECIAL_MESSAGE) {
    ... special code here ...
    return TRUE; // handled
  }
  return FALSE; // not handled
}

Could that be the problem?"

Yes, that's the problem.

The customer saw the recommendation to use Post­Message instead of Post­Thread­Message but simply blindly followed the advice rather than understanding its rationale so they could apply the advice correctly.

If you read the original recommendation, you'll see that the problem is that when a modal loop runs, your message loop is no longer in control, and therefore any customizations you've made to your message loop will not be in effect. This is normally a good thing. For example, if a dialog box calls Message­Box, the dialog keyboard shortcuts shouldn't be active while the message box is displayed. It would be very strange if hitting Enter caused the dialog box to invoke its default button while the modal message box is still on the screen. The result would most likely be a dialog box without underlying support, which leads to unhappiness.

If there is some sort of message processing you want to happen regardless of which message loop is control, then you can't put it in your custom message loop because (tautologically) your custom message loop is not in control when it is not running. But message loops will call Dispatch­Message, and that will deliver the message to your window procedure. (Of course, the converse also applies: If you want the behavior to be suspended when a modal operation is in progress, you can put it in your message loop.)

Comments (8)
  1. Peter says:

    I don't really blame the customer for this one.  This sort of issue is inherent in using a framework like MFC.  It can be very hard to understand the framework's behaviour unless you also understand its implementation details.

  2. Koro says:

    If you absolutely need to take control when a system message loop is running though, you can register a WH_MSGFILTER hook. Every well-behaved message loop should call CallMsgFilter. (Sadly, I'd say 90% of message loops don't, because all samples just have the GetMessage/TranslateMessage/DispatchMessage trio)

  3. RangerFish says:

    It's certainly a problem that a lot of documentation lacks the background details, but I often purposely "redact" information from documentation, questions, answers and general comments. The reason is I've had too many conversations like this:

    Someone Else: Why does <insert scenario here> happen?

    Me          : Because of A. <insert in-depth explanation of A>

    Someone Else: Oh, couldn't you have done something else instead of A?

    Me:         : No, because of B.

    Someone Else: Oh, couldn't you have done something else instead of B?

    Me:         : No, because of C.

    Someone Else: Oh, couldn't you have done something else instead of C?

    Me:         : No, because of D.

    <4 months later>

    Someone Else: Oh, couldn't you have done something else instead of Z?

    Me          : I'm losing the will to live.

    I think a lot of the time stuff is left out of documentation specifically to avoid this kind of conversation.

  4. Rick C says:

    @Peter, MFC doesn't appear to be the problem here.

  5. > Simply blindly followed the advice rather than understanding

    > its rationale so they could apply the advice correctly.

    My biggest complaint about almost all API documentation, (MS's and every-one else's) is the reluctance to explain the rationale.  I understand it's a lot more work and tends to require a real technical writer, but it makes a huge difference to its use.

    On the other hand, when I endeavored to ensure that a proper rationale was included with my company's API documentation, we got complaints about how "we give us the minimum to use it – we don't have the time or expertise to understand it…"

    *sigh*.

  6. Gabe says:

    Did the customer have any rationale for putting their custom message processing in PreTranslateMessage instead of the window procedure? I can't think of any good reason for doing that.

    [I suspect they simply didn't know any better. "We need to process this message. Hey, look, here's a place where we can process messages." -Raymond]
  7. Neil says:

    All this modal loop processing is all very well as long as your application can never have more than one enabled window at once.

    For software such as Outlook which can readily have multiple enabled windows it breaks down almost immediately.

  8. @Neil,

    Each top level window which can call a modal function needs to belong to a different thread, if you want to be able to have independent modal dialogs. Or they need to be owned by an invisible top level window, and before entering a modal loop they all need to be disabled. Otherwise it definitely becomes very fragile.

Comments are closed.