{"id":41863,"date":"2003-11-12T03:23:00","date_gmt":"2003-11-12T03:23:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2003\/11\/12\/a-different-type-of-dialog-procedure\/"},"modified":"2003-11-12T03:23:00","modified_gmt":"2003-11-12T03:23:00","slug":"a-different-type-of-dialog-procedure","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20031112-00\/?p=41863","title":{"rendered":"A different type of dialog procedure"},"content":{"rendered":"<p>\nIn the discussion following my entry about dialog procedure return\nvalues, somebody suggested an alternate dialog design where\nyou just call <code>DefDlgProc<\/code> to do default actions\n(the same way you write window procedures and <code>DefWindowProc<\/code>)\nrather than returning TRUE\/FALSE.\n<\/p>\n<p>\nSo let&#8217;s do that. In fact, we&#8217;re going to do it twice.\nI&#8217;ll cover one method today and cover an entirely different\nmethod later this week. Each method consists of a simple kernel of\nan idea; the rest is just scaffolding to make the kernel work.\n<\/p>\n<p>\nThe first way uses a recursive call from the\ndialog procedure back into\n<code>DefDlgProc<\/code> to trigger the default behavior.\nThis technique requires that you have a flag that lets you detect\n(and therefore break) the recursion.\nSince you typically have instance data attached to your dialog box\nanyway, it&#8217;s not too hard to add another member to it.\n<\/p>\n<p>\nThe kernel\nis to &#8220;subvert the recursive call&#8221;. <code>DefDlgProc<\/code>\ncalls your dialog procedure to see what you want to do.\nWhen you want to do the default action, just call\n<code>DefDlgProc<\/code> recursively. The inner <code>DefDlgProc<\/code>\nwill call your dialog procedure to see if you want to override the\ndefault action. Detect this recursive call and return\nFALSE (&#8220;do the default&#8221;).\nThe recursive <code>DefDlgProc<\/code>\nwill then perform the default action and\nreturn its result. Now you have the result of the default action,\nand you can modify it or augment it before returning that as\nthe result for\nthe dialog box procedure, back to the outer <code>DefDlgProc<\/code>\nwhich returns that value back as the final message result.\n<\/p>\n<p>\nHere&#8217;s the flow diagram, for those who prefer pictures:\n<\/p>\n<pre>\nMessage delivered\n-&gt; DefDlgProc\n   -&gt; your dialog procedure\n      decide what to do\n      want to do the default action\n      -&gt; DefDlgProc\n         -&gt; your dialog procedure\n            detect recursion\n         &lt;- return FALSE\n         DefDlgProc sees FALSE\n         performs default behavior\n      &lt;- returns result of default behavior\n      you do other stuff (perhaps modify\n      default behavior after it occurred)\n      set DWLP_MSGRESULT to desired result\n   &lt;- return TRUE\n   retrieve DWLP_MSGRESULT\n&lt;- return it as message result\n<\/pre>\n<p>\nGiven this sketch, you should be able to write it up yourself.\nHere&#8217;s what I came up with.  I call it a Wndproc-Like Dialog:\n<\/p>\n<pre>\nclass WLDialogBox\n{\npublic:\n  virtual LRESULT WLDlgProc(\n            HWND hdlg, UINT uMsg,\n            WPARAM wParam, LPARAM lParam)\n  {\n    return DefDlgProcEx(hdlg, uMsg, wParam, lParam,\n                        &amp;m_fRecursing);\n  }\n  INT_PTR DoModal(HINSTANCE hinst, LPCTSTR pszTemplate,\n                  HWND hwndParent)\n  {\n    m_fRecursing = FALSE;\n    return DialogBoxParam(hinst, pszTemplate, hwndParent,\n                          s_DlgProc, (LPARAM)this);\n  }\nprivate:\n  static INT_PTR CALLBACK s_DlgProc(\n            HWND hdlg, UINT uMsg,\n            WPARAM wParam, LPARAM lParam)\n  {\n    if (uMsg == WM_INITDIALOG) {\n      SetWindowLongPtr(hdlg, DWLP_USER, lParam);\n    }\n    WLDialogBox *self = (WLDialogBox*)GetWindowLongPtr(\n                            hdlg, DWLP_USER);\n    if (!self) {\n      return FALSE;\n    }\n    CheckDefDlgRecursion(&amp;self-&gt;m_fRecursing);\n    return SetDlgMsgResult(hdlg, uMsg,\n              self-&gt;WLDlgProc(\n                hdlg, uMsg, wParam, lParam));\n  }\nprivate:\n  BOOL m_fRecursing;\n};\n<\/pre>\n<p>\nLet&#8217;s walk through this class.\n<\/p>\n<p>\nThe <code>WLDlgProc<\/code> method is virtual because we expect\nderived classes to do custom actions in their dialog procedure\nthat we invoke from our <code>s_DlgProc<\/code>.\nThe default implementation in the base class\nuses the <code>DefDlgProcEx<\/code>\nmacro from <code>windowsx.h<\/code> to do the dirty work.\nThat&#8217;s right, this technique has been published by Microsoft\nsince 1992. If you look at <code>DefDlgProcEx<\/code>, it sets the\nrecursion flag to TRUE and then calls <code>DefDlgProc<\/code>,\nwhich triggers the recursive call.\n<\/p>\n<p>\nI could have had a separate <code>WLDefDlgProc<\/code> method which\ncalls <code>DefDlgProcEx<\/code> and have <code>WLDlgProc<\/code> call\n<code>WLDefDlgProc<\/code>. (In fact, my first version did exactly that.)\nBut I decided not to have a <code>WLDefDlgProc<\/code> to remove the\ntemptation to bypass the base class&#8217;s <code>WLDefDlgProc<\/code>.\nInstead, if you want default handling to take place, forward the\ncall to your base class&#8217;s <code>WLDefDlgProc<\/code>.\n<\/p>\n<p>\nThe <code>s_DlgProc<\/code> method is the dialog procedure used for\nall instances of Wndproc-Like dialogs. It initializes itself in\nthe <code>WM_INITDIALOG<\/code> message so future messages can identify\nwhich instance of the dialog is handling the message.\nAfter short-circuiting messages that arrive before the dialog box\nhas initialized, it uses the <code>CheckDlgRecursion<\/code> macro,\nalso from <code>windowsx.h<\/code>. This macro checks the recursion\nflag; if set, then it resets the flag and just returns FALSE immediately.\nThis is what stops the recursion. Otherwise, it calls the\n<code>WLDlgProc<\/code> method (which has probably been overriden in\na derived class), then sets the dialog procedure return value and\nreturns.\n<\/p>\n<p>\nThe <code>SetDlgMsgResult<\/code> macro also comes from\n<code>windowsx.h<\/code>: It stores the return value into the\n<code>DWLP_MSGRESULT<\/code> and returns TRUE.  Well, unless the\nmessage is one of the special exceptions, in which case it returns\nthe value directly.  <strong>Note to 64-bit developers<\/strong>: There\nis a bug in this macro as currently written. The expression\n<code>(BOOL)(result)<\/code> should be changed to\n<code>(INT_PTR)(result)<\/code> so that the upper 32 bits of the return\nvalue is not truncated.\n<\/p>\n<p>\nThe last method is <code>DoModal<\/code>, which initializes the\nrecursion flag and kicks off the dialog box.\n<\/p>\n<p>\nHere&#8217;s a sample program that illustrates the use of this class:<\/p>\n<pre>\n<\/p>\nclass SampleWLDlg : public WLDialogBox\n{\n  LRESULT WLDlgProc(HWND hdlg, UINT uMsg,\n            WPARAM wParam, LPARAM lParam)\n  {\n    switch (uMsg) {\n    HANDLE_MSG(hdlg, WM_COMMAND, OnCommand);\n    HANDLE_MSG(hdlg, WM_SETCURSOR, OnSetCursor);\n    }\n    return __super::WLDlgProc(hdlg, uMsg, wParam, lParam);\n  };\n  void OnCommand(HWND hdlg, int id,\n                 HWND hwndCtl, UINT codeNotify)\n  {\n    switch (id) {\n    case IDCANCEL:\n      MessageBox(hdlg, TEXT(\"Bye\"), TEXT(\"Title\"), MB_OK);\n      EndDialog(hdlg, 1);\n      break;\n    }\n  }\n  BOOL OnSetCursor(HWND hdlg, HWND hwndCursor,\n                   UINT codeHitTest, UINT msg)\n  {\n    if (codeHitTest == HTCAPTION) {\n      SetCursor(LoadCursor(NULL, IDC_SIZEALL));\n      return TRUE;\n    }\n    return FORWARD_WM_SETCURSOR(hdlg, hwndCursor,\n                  codeHitTest, msg, __super::WLDlgProc);\n  }\n};\nint WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev,\n                   LPSTR lpCmdLine, int nShowCmd)\n{\n    SampleWLDlg dlg;\n    dlg.DoModal(hinst, MAKEINTRESOURCE(1), NULL);\n    return 0;\n}\n1 DIALOGEX DISCARDABLE  0, 0, 200,200\nSTYLE DS_SHELLFONT | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU\nCAPTION \"sample\"\nFONT 8, \"MS Shell Dlg\"\nBEGIN\nDEFPUSHBUTTON \"&amp;Bye\",IDCANCEL,\"Button\",WS_TABSTOP,7,4,50,14\nEND\n<\/pre>\n<p>\nTo illustrate a custom return value, I override the <code>WM_SETCURSOR<\/code>\nmessage to display a custom cursor when the mouse is over the caption area.\nIt&#8217;s not exciting, but it gets the point across.\n<\/p>\n<p>\nObserve that in two places, we invoked\nthe default handler by calling <code>__super::WLDlgProc<\/code>.\n<code>__super<\/code> is a Visual C++ extension that resolves to\nthe base class of your derived class. This is quite handy since it\nsaves the reader the trouble of figure out &#8220;So which level in the class\nhierarchy are we forwarding this call to?&#8221;  If you wanted to forward\na call to your grandparent class, you would use\n<code>__super::__super::WLDlgProc<\/code>.\n<\/p>\n<p>\nIf your compiler doesn&#8217;t support <code>__super<\/code>, you can fake it\nby adding this line to the definition of <code>SampleWLDlg<\/code>:\n<\/p>\n<pre>\n  typedef WLDialogBox super;\n<\/pre>\n<p>\nand using <code>super::WLDlgProc<\/code> without the underscores.\nIn fact, this is the technique I use because I was doing it\nbefore the VC folks added the <code>__super<\/code> keyword and\nnow it&#8217;s just habit.\n<\/p>\n<p><strong>Exercise<\/strong>: Does the <code>m_fRecursing<\/code> member\nreally have to be per-instance? Can it be global?<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In the discussion following my entry about dialog procedure return values, somebody suggested an alternate dialog design where you just call DefDlgProc to do default actions (the same way you write window procedures and DefWindowProc) rather than returning TRUE\/FALSE. So let&#8217;s do that. In fact, we&#8217;re going to do it twice. I&#8217;ll cover one method [&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-41863","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>In the discussion following my entry about dialog procedure return values, somebody suggested an alternate dialog design where you just call DefDlgProc to do default actions (the same way you write window procedures and DefWindowProc) rather than returning TRUE\/FALSE. So let&#8217;s do that. In fact, we&#8217;re going to do it twice. I&#8217;ll cover one method [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/41863","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=41863"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/41863\/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=41863"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=41863"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=41863"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}