A customer wanted to know how to change a dialog box’s message loop so that it used MsgÂWaitÂForÂMultipleÂObjects instead of GetÂMessage. (I’m guessing that they had a handle that they wanted to wait on while the dialog was up.) The standard dialog box message loop checks only for messages, not kernel handles.
One way to do it is to replace the modal dialog box with a modeless one and run your own message loop. However, there’s a key piece missing from the do-it-yourself, which is that there is no way to know whether the dialog procedure has called EndÂDialog, and if so, what result code it passed.
So maybe you’re displaying somebody else’s dialog box and therefore cannot alter the dialog procedure to set a different flag when it wants to end the dialog. What can you do to customize the dialog loop from the outside?
Dialog boxes send their owner the WM_ message when they have run out of work to do and are about to block waiting for a message. if the handler of the WM_ message returns, and there is no posted message in the queue, then the dialog box goes to sleep and waits for a message to come in.
As the name of the message suggests, one way to use the WM_ message is to handle the message by doing do background idle-time activity, and then return when there is a message for the dialog box to process. For example, maybe you want to do spell checking when the user is idle.
Another way to use the WM_ message is to take over how the dialog message loop waits for a message. You can do your own thing, and return when there is a posted message that needs processing.
So let’s try it. Just for demonstration purposes, we’ll create a waitable timer that beeps every two seconds while a common file open dialog is up.
Start with our scratch program and make these changes. Remember that error checking has been elided for expository purposes.
#include <commdlg.h>
HANDLE hTimer;
void OnChar(HWND hwnd, TCHAR ch, int cRepeat)
{
if (ch != ' ') return;
hTimer = CreateWaitableTimerW(nullptr, FALSE, nullptr);
LARGE_INTEGER twoSeconds;
twoSeconds.QuadPart = -2 * wil::filetime_duration::one_second;
SetWaitableTimer(h, &twoSeconds, 2000, nullptr, nullptr, FALSE);
TCHAR buffer[MAX_PATH]{};
OPENFILENAME ofn{};
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = hwnd;
ofn.lpstrFilter = TEXT("All Files\0*.*\0");
ofn.nFilterIndex = 1;
ofn.lpstrFile = buffer;
ofn.nMaxFile = ARRAYSIZE(buffer);
GetOpenFileName(&ofn);
CloseHandle(hTimer);
hTimer = nullptr;
}
Our WM_ handler ignores all characters other than the space bar. But if you press the space bar, it creates a waitable timer that triggers every two seconds, and then it displays a dialog: In this case, it’s the common file open dialog. When the dialog is finished, we close the timer since we don’t need it any more. This is just setting up the environment for our WM_ handler to do its magic.
The interesting work happens in the next function.
void OnEnterIdle(HWND hwnd, UINT source, HWND hwndSource)
{
if (!hTimer) return;
MSG msg;
while (true) {
DWORD result = MsgWaitForMultipleObjects(1, &hTimer,
FALSE, INFINITE, QS_ALLINPUT);
switch (result) {
case WAIT_OBJECT_0:
MessageBeep(~0);
break;
case WAIT_OBJECT_0 + 1:
if (PeekMessage(&msg, nullptr, 0, 0, PM_NOREMOVE)) {
return;
}
break;
default:
FAIL_FAST_HR(E_UNEXPECTED);
}
}
}
When we get a WM_ message and there is no timer handle, then just return without doing anything, allowing the dialog box message loop to go idle.
But if we have a timer handle, then we use the MsgÂWaitÂForÂMultipleÂObcjets function to wait for either the timer handle to be signaled or for a message to arrive. If it was the timer handle, we beep. If it was a message, we call PeekÂMessage to process any inbound SendÂMessage calls and see if there’s a posted message waiting. if so, then we leave it in the message queue (PM_) for the dialog loop to pick up and return. If we get any other code, then something went horrible wrong, and we fail fast.
Now we can hook these up to our window procedure.
HANDLE_MSG(hwnd, WM_CHAR, OnChar);
HANDLE_MSG(hwnd, WM_ENTERIDLE, OnEnterIdle);
When the common dialog box goes idle, the internal dialog message pump sends a WM_ message to the owner (which is us), and we do our handle stuff until a posted message is ready.
When you run this program, press the space bar, and you get a happy common dialog box, accompanied by some annoying beeping.
I pulled a trick here. Maybe you noticed it. We’ll look at it next time.
0 comments
Be the first to start the discussion.