{"id":112165,"date":"2026-03-25T07:00:00","date_gmt":"2026-03-25T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=112165"},"modified":"2026-03-25T06:50:04","modified_gmt":"2026-03-25T13:50:04","slug":"20260325-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20260325-00\/?p=112165","title":{"rendered":"How can I change a dialog box&#8217;s message loop to do a <CODE>Msg&shy;Wait&shy;For&shy;Multiple&shy;Objects<\/CODE> instead of <CODE>Get&shy;Message<\/CODE>?"},"content":{"rendered":"<p>A customer wanted to know how to change a dialog box&#8217;s message loop so that it used <code>Msg\u00adWait\u00adFor\u00adMultiple\u00adObjects<\/code> instead of <code>Get\u00adMessage<\/code>. (I&#8217;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.<\/p>\n<p>One way to do it is to replace the modal dialog box with a modeless one and <a title=\"The dialog manager, part 4: The dialog loop\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20050401-00\/?p=35993\"> run your own message loop<\/a>. However, there&#8217;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 <code>End\u00adDialog<\/code>, and if so, what result code it passed.<\/p>\n<p>So maybe you&#8217;re displaying somebody else&#8217;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?<\/p>\n<p>Dialog boxes send their owner the <code>WM_<wbr \/>ENTER\u00adIDLE<\/code> message when they have run out of work to do and are about to block waiting for a message. if the handler of the <code>WM_<wbr \/>ENTER\u00adIDLE<\/code> 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.<\/p>\n<p>As the name of the message suggests, one way to use the <code>WM_<wbr \/>ENTER\u00adIDLE<\/code> 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.<\/p>\n<p>Another way to use the <code>WM_<wbr \/>ENTER\u00adIDLE<\/code> 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.<\/p>\n<p>So let&#8217;s try it. Just for demonstration purposes, we&#8217;ll create a waitable timer that beeps every two seconds while a common file open dialog is up.<\/p>\n<p>Start with our <a title=\"The scratch program\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20030723-00\/?p=43073\"> scratch program<\/a> and make these changes. Remember that error checking has been elided for expository purposes.<\/p>\n<pre>#include &lt;commdlg.h&gt;\r\n\r\nHANDLE hTimer;\r\n\r\nvoid OnChar(HWND hwnd, TCHAR ch, int cRepeat)\r\n{\r\n    if (ch != ' ') return;\r\n\r\n    hTimer = CreateWaitableTimerW(nullptr, FALSE, nullptr);\r\n\r\n    LARGE_INTEGER twoSeconds;\r\n    twoSeconds.QuadPart = -2 * wil::filetime_duration::one_second;\r\n    SetWaitableTimer(h, &amp;twoSeconds, 2000, nullptr, nullptr, FALSE);\r\n\r\n    TCHAR buffer[MAX_PATH]{};\r\n    OPENFILENAME ofn{};\r\n    ofn.lStructSize = sizeof(ofn);\r\n    ofn.hwndOwner = hwnd;\r\n    ofn.lpstrFilter = TEXT(\"All Files\\0*.*\\0\");\r\n    ofn.nFilterIndex = 1;\r\n    ofn.lpstrFile = buffer;\r\n    ofn.nMaxFile = ARRAYSIZE(buffer);\r\n    GetOpenFileName(&amp;ofn);\r\n\r\n    CloseHandle(hTimer);\r\n    hTimer = nullptr;\r\n}\r\n<\/pre>\n<p>Our <code>WM_<wbr \/>CHAR<\/code> 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&#8217;s the common file open dialog. When the dialog is finished, we close the timer since we don&#8217;t need it any more. This is just setting up the environment for our <code>WM_<wbr \/>ENTER\u00adIDLE<\/code> handler to do its magic.<\/p>\n<p>The interesting work happens in the next function.<\/p>\n<pre>void OnEnterIdle(HWND hwnd, UINT source, HWND hwndSource)\r\n{\r\n    if (!hTimer) return;\r\n\r\n    MSG msg;\r\n    while (true) {\r\n        DWORD result = MsgWaitForMultipleObjects(1, &amp;hTimer,\r\n                                FALSE, INFINITE, QS_ALLINPUT);\r\n        switch (result) {\r\n        case WAIT_OBJECT_0:\r\n            MessageBeep(~0);\r\n            break;\r\n\r\n        case WAIT_OBJECT_0 + 1:\r\n            if (PeekMessage(&amp;msg, nullptr, 0, 0, PM_NOREMOVE)) {\r\n                return;\r\n            }\r\n            break;\r\n\r\n        default:\r\n            FAIL_FAST_HR(E_UNEXPECTED);\r\n        }\r\n    }\r\n}\r\n<\/pre>\n<p>When we get a <code>WM_<wbr \/>ENTER\u00adIDLE<\/code> message and there is no timer handle, then just return without doing anything, allowing the dialog box message loop to go idle.<\/p>\n<p>But if we have a timer handle, then we use the <code>Msg\u00adWait\u00adFor\u00adMultiple\u00adObcjets<\/code> 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 <code>Peek\u00adMessage<\/code> to process any inbound <code>Send\u00adMessage<\/code> calls and see if there&#8217;s a posted message waiting. if so, then we leave it in the message queue (<code>PM_<wbr \/>NO\u00adREMOVE<\/code>) for the dialog loop to pick up and return. If we get any other code, then something went horrible wrong, and we fail fast.<\/p>\n<p>Now we can hook these up to our window procedure.<\/p>\n<pre>    HANDLE_MSG(hwnd, WM_CHAR, OnChar);\r\n    HANDLE_MSG(hwnd, WM_ENTERIDLE, OnEnterIdle);\r\n<\/pre>\n<p>When the common dialog box goes idle, the internal dialog message pump sends a <code>WM_<wbr \/>ENTER\u00adIDLE<\/code> message to the owner (which is us), and we do our handle stuff until a posted message is ready.<\/p>\n<p>When you run this program, press the space bar, and you get a happy common dialog box, accompanied by some annoying beeping.<\/p>\n<p>I pulled a trick here. Maybe you noticed it. We&#8217;ll look at it next time.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The dialog box lets you change how it waits.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-112165","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>The dialog box lets you change how it waits.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112165","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=112165"}],"version-history":[{"count":1,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112165\/revisions"}],"predecessor-version":[{"id":112166,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112165\/revisions\/112166"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=112165"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=112165"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=112165"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}