Last time, I left you with a homework exercise: Find the subtle bug in the interaction between EndManualModalDialog and the modal message loop.

The subtlety is that EndManualModalDialog sets some flags but does nothing to force the message loop to notice that the flag was actually set. Recall that the GetMessage function does not return until a posted message arrives in the queue. If incoming sent messages arrive, they are delivered to the corresponding window procedure, but the GetMessage function doesn't return. It just keeps delivering incoming sent messages until a posted message finally arrives.

The bug, therefore, is that when you call EndManualModalDialog, it sets the flag that tells the modal message loop to stop running, but doesn't do anything to ensure that the modal message loop will wake up to notice. Nothing happens until a posted message arrives, which causes GetMessage to return. The posted message is dispatched and the while loop restarted, at which point the code finally notices that the fEnded flag is set and breaks out of the modal message loop.

There are a few ways of fixing this problem. The quick solution is to post a meaningless message.

void EndManualModalDialog(HWND hdlg, int iResult)
{
 DIALOGSTATE *pds = reinterpret_cast<DIALOGSTATE*>
     (GetWindowLongPtr(hdlg, DWLP_USER));
 if (pds) {
  pds->iResult = iResult;
  pds->fEnded = TRUE;
  PostMessage(hdlg, WM_NULL, 0, 0);
 }
}

This will force the GetMessage to return, since we made sure there is at least one posted message in the queue waiting to be processed. We chose the WM_NULL message because it doesn't do anything. We aren't interested in what the message does, just the fact that there is a message at all.

Next time, a different solution to the same problem.