{"id":35983,"date":"2005-04-04T08:56:48","date_gmt":"2005-04-04T08:56:48","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2005\/04\/04\/the-dialog-manager-part-5-converting-a-non-modal-dialog-box-to-modal\/"},"modified":"2025-04-23T07:55:24","modified_gmt":"2025-04-23T14:55:24","slug":"the-dialog-manager-part-5-converting-a-non-modal-dialog-box-to-modal","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20050404-48\/?p=35983","title":{"rendered":"The dialog manager, part 5: Converting a non-modal dialog box to modal"},"content":{"rendered":"<p>Let&#8217;s apply <a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20050401-00\/?p=35993\"> what we learned from last time<\/a> and convert a modeless dialog box into a modal one. As always, <a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20030723-00\/?p=43073\"> start with the scratch program<\/a> and make the following additions:<\/p>\n<pre>INT_PTR CALLBACK DlgProc(\r\n    HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)\r\n{\r\n switch (uMsg) {\r\n case WM_INITDIALOG:\r\n  SetWindowLongPtr(hdlg, DWLP_USER, lParam);\r\n  return TRUE;\r\n case WM_COMMAND:\r\n  switch (GET_WM_COMMAND_ID(wParam, lParam)) {\r\n  case IDOK:\r\n   EndDialog(hdlg, 2005);\r\n   break;\r\n  case IDCANCEL:\r\n   EndDialog(hdlg, 1776);\r\n   break;\r\n  }\r\n }\r\n return FALSE;\r\n}\r\nint DoModal(HWND hwnd)\r\n{\r\n return DialogBox(g_hinst, MAKEINTRESOURCE(1), hwnd, DlgProc);\r\n}\r\nvoid OnChar(HWND hwnd, TCHAR ch, int cRepeat)\r\n{\r\n switch (ch) {\r\n case ' ': DoModal(hwnd); break;\r\n }\r\n}\r\n\/\/ Add to WndProc\r\n   HANDLE_MSG(hwnd, WM_CHAR, OnChar);\r\n\/\/ Resource file\r\n1 DIALOGEX DISCARDABLE  32, 32, 200, 40\r\nSTYLE DS_MODALFRAME | DS_SHELLFONT | WS_POPUP |\r\n      WS_VISIBLE | WS_CAPTION | WS_SYSMENU\r\nCAPTION \"Sample\"\r\nFONT 8, \"MS Shell Dlg\"\r\nBEGIN\r\n DEFPUSHBUTTON \"OK\",IDOK,20,20,50,14\r\n PUSHBUTTON \"Cancel\",IDCANCEL,74,20,50,14\r\nEND\r\n<\/pre>\n<p>Not a very exciting program, I grant you that. It just displays a dialog box and returns a value that depends on which button you pressed. The <code>DoModal<\/code> function uses <a href=\"http:\/\/msdn.microsoft.com\/library\/en-us\/winui\/winui\/windowsuserinterface\/windowing\/dialogboxes\/dialogboxreference\/dialogboxfunctions\/dialogbox.asp\"> the <code>DialogBox<\/code> function<\/a> to do the real work.<\/p>\n<p>Now let&#8217;s convert the <code>DoModal<\/code> function so it implements the modal loop directly. Why? Just to see how it&#8217;s done. In real life, of course, there would normally be no reason to undertake this exercise; the dialog box manager does a fine job.<\/p>\n<p>First, we need to figure out where we&#8217;re going to keep track of the flag we called &lt;dialog still active&gt; last time. We&#8217;ll keep it in a structure that we hang off the dialog box&#8217;s <code>DWLP_USER<\/code> window bytes. (I sort of planned ahead for this by having the <code>DlgProc<\/code> function stash the <code>lParam<\/code> into the <code>DWLP_USER<\/code> extra bytes when the dialog is initialized.)<\/p>\n<pre>\/\/ fEnded tells us if the dialog has been ended.\r\n\/\/ When ended, iResult contains the result code.\r\ntypedef struct DIALOGSTATE {\r\n BOOL fEnded;\r\n int iResult;\r\n} DIALOGSTATE;\r\nvoid EndManualModalDialog(HWND hdlg, int iResult)\r\n{\r\n DIALOGSTATE *pds = reinterpret_cast&lt;DIALOGSTATE*&gt;\r\n     (GetWindowLongPtr(hdlg, DWLP_USER));\r\n if (pds) {\r\n  pds-&gt;iResult = iResult;\r\n  pds-&gt;fEnded = TRUE;\r\n }\r\n}\r\n<\/pre>\n<p>The <code>EndManualModalDialog<\/code> takes the place of <a href=\"http:\/\/msdn.microsoft.com\/library\/en-us\/winui\/winui\/windowsuserinterface\/windowing\/dialogboxes\/dialogboxreference\/dialogboxfunctions\/enddialog.asp\"> the <code>EndDialog<\/code> function<\/a>: Instead of updating the dialog manager&#8217;s internal &#8220;is the dialog finished?&#8221; flag, we update ours.<\/p>\n<p>All we have to do to convert our <code>DlgProc<\/code> from one using the dialog manager&#8217;s modal loop to our custom modal loop, then, is to change the calls to <code>EndDialog<\/code> to call our function instead.<\/p>\n<pre>INT_PTR CALLBACK DlgProc(\r\n    HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)\r\n{\r\n switch (uMsg) {\r\n case WM_INITDIALOG:\r\n  SetWindowLongPtr(hdlg, DWLP_USER, lParam);\r\n  return TRUE;\r\n case WM_COMMAND:\r\n  switch (GET_WM_COMMAND_ID(wParam, lParam)) {\r\n  case IDOK:\r\n   <span style=\"color: blue;\">EndManualModeDialog<\/span>(hdlg, 2005);\r\n   break;\r\n  case IDCANCEL:\r\n   <span style=\"color: blue;\">EndManualModeDialog<\/span>(hdlg, 1776);\r\n   break;\r\n  }\r\n }\r\n return FALSE;\r\n}\r\n<\/pre>\n<p>All that&#8217;s left is to write the custom dialog message loop.<\/p>\n<pre>int DoModal(HWND hwnd)\r\n{\r\n DIALOGSTATE ds = { 0 };\r\n HWND hdlg = CreateDialogParam(g_hinst, MAKEINTRESOURCE(1),\r\n             hwnd, DlgProc, reinterpret_cast&lt;LPARAM&gt;(&amp;ds));\r\n if (!hdlg) {\r\n  return -1;\r\n }\r\n EnableWindow(hwnd, FALSE);\r\n MSG msg;\r\n msg.message = WM_NULL; \/\/ anything that isn't WM_QUIT\r\n while (!ds.fEnded &amp;&amp; GetMessage(&amp;msg, NULL, 0, 0)) {\r\n  if (!IsDialogMessage(hdlg, &amp;msg)) {\r\n   TranslateMessage(&amp;msg);\r\n   DispatchMessage(&amp;msg);\r\n  }\r\n }\r\n if (msg.message == WM_QUIT) {\r\n  PostQuitMessage((int)msg.wParam);\r\n }\r\n EnableWindow(hwnd, TRUE);\r\n DestroyWindow(hdlg);\r\n return ds.iResult;\r\n}\r\n<\/pre>\n<p>Most of this should make sense given what we&#8217;ve learned over the past few days.<\/p>\n<p>We start by creating the dialog modelessly, passing a pointer to our dialog state as the creation parameter, which as we noted earlier, our dialog procedure squirrels away in the <code>DWLP_USER<\/code> window bytes for <code>EndManualModalDialog<\/code> to use.<\/p>\n<p>Next we disable the owner window; this is done after creating the modeless dialog, observing <a href=\"http:\/\/weblogs.asp.net\/oldnewthing\/archive\/2004\/02\/27\/81155.aspx\"> the rules for enabling and disabling windows<\/a>. We then fall into our message loop, which looks exactly like what we said it should look. All we did was substitute <code>!ds.fEnded<\/code> for the pseudocode &lt;dialog still active&gt;. After the modal loop is done, we continue with the standard bookkeeping: Re-posting any quit message, re-enabling the owner before destroying the dialog, then returning the result.<\/p>\n<p>As you can see, the basics of modal dialogs are really not that exciting. But now that you have this basic framework, you can start tinkering with it.<\/p>\n<p>First, however, your homework is to find a bug in the above code. It&#8217;s rather subtle. Hint: Look closely at the interaction between <code>EndManualModalDialog<\/code> and the modal message loop.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Let&#8217;s apply what we learned from last time and convert a modeless dialog box into a modal one. As always, start with the scratch program and make the following additions: INT_PTR CALLBACK DlgProc( HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_INITDIALOG: SetWindowLongPtr(hdlg, DWLP_USER, lParam); return TRUE; case WM_COMMAND: switch [&hellip;]<\/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-35983","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Let&#8217;s apply what we learned from last time and convert a modeless dialog box into a modal one. As always, start with the scratch program and make the following additions: INT_PTR CALLBACK DlgProc( HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_INITDIALOG: SetWindowLongPtr(hdlg, DWLP_USER, lParam); return TRUE; case WM_COMMAND: switch [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/35983","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=35983"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/35983\/revisions"}],"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=35983"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=35983"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=35983"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}