{"id":1893,"date":"2014-02-03T07:00:00","date_gmt":"2014-02-03T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2014\/02\/03\/how-can-i-make-a-wndproc-or-dlgproc-a-member-of-my-c-class\/"},"modified":"2014-02-03T07:00:00","modified_gmt":"2014-02-03T07:00:00","slug":"how-can-i-make-a-wndproc-or-dlgproc-a-member-of-my-c-class","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20140203-00\/?p=1893","title":{"rendered":"How can I make a WNDPROC or DLGPROC a member of my C++ class?"},"content":{"rendered":"<p>\nContinuing my discussion of\n<a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2014\/01\/27\/10492898.aspx\">\n<i>How can I make a callback function a member of my C++ class?<\/i><\/a>\n<\/p>\n<p>\nCommon special cases for wanting to use a member function as a\ncallback function are the window procedure and its cousin the\ndialog procedure.\nThe question, then, is where to put the reference data.\n<\/p>\n<p>\nLet&#8217;s start with window procedures.\nThe <code>Create&shy;Window<\/code> function and its close friend\n<code>Create&shy;Window&shy;Ex<\/code> let you pass your reference\ndata as the final parameter, prototyped as\n<code>LPVOID lpParam<\/code>.\nAs noted in the documentation,\nthat value is passed back to the window procedure by the\n<code>WM_NC&shy;CREATE<\/code> and\n<code>WM_CREATE<\/code> messages\nas part of the <code>CREATE&shy;STRUCT<\/code> structure.\nOne of the first messages passed to a window is\n<code>WM_NC&shy;CREATE<\/code>,\nso that&#8217;s where we&#8217;ll grab the reference data and save it for later.\n<\/p>\n<p>\nYou can follow along\n<a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2005\/04\/22\/410773.aspx\">\nin this simple C++ program<\/a>:\nThe static window procedure handles the\n<code>WM_NC&shy;CREATE<\/code> message by\nextracting the <code>lpCreate&shy;Params<\/code> from the\n<code>CREATE&shy;STRUCT<\/code> and saving it in the\n<code>GWLP_USER&shy;DATA<\/code> window bytes.\nThat value is a special per-window storage location provided\nfor the benefit of the window procedure,\nand most people use it to store their context parameter for\nsafekeeping.\n<\/p>\n<p>\nIf the message is not\n<code>WM_NC&shy;CREATE<\/code>,\nthen we retrieve the context parameter from\nwhere we had stashed it.\n<\/p>\n<p>\nEither way, we end up with a copy of the context parameter.\nIf you want your window procedure to be a member function,\nthe natural choice for the context parameter is the <code>this<\/code>\npointer for the instance.\nThe static window procedure therefore tends to look like this:\n<\/p>\n<pre>\nLRESULT CALLBACK MyWindowClass::s_WndProc(\n    HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)\n{\n MyWindowClass *pThis; \/\/ our \"this\" pointer will go here\n if (uMsg == WM_NCCREATE) {\n  \/\/ Recover the \"this\" pointer which was passed as a parameter\n  \/\/ to CreateWindow(Ex).\n  LPCREATESTRUCT lpcs = reinterpret_cast&lt;LPCREATESTRUCT&gt;(lParam);\n  pThis = static_cast&lt;MyWindowClass*&gt;(lpcs-&gt;lpCreateParams);\n  \/\/ Put the value in a safe place for future use\n  SetWindowLongPtr(hwnd, GWLP_USERDATA,\n                   reinterpret_cast&lt;LONG_PTR&gt;(pThis));\n } else {\n  \/\/ Recover the \"this\" pointer from where our WM_NCCREATE handler\n  \/\/ stashed it.\n  pThis = reinterpret_cast&lt;MyWindowClass*&gt;(\n              GetWindowLongPtr(hwnd, GWLP_USERDATA));\n }\n if (pThis) {\n  \/\/ Now that we have recovered our \"this\" pointer, let the\n  \/\/ member function finish the job.\n  return pThis-&gt;WndProc(hwnd, uMsg, wParam, lParam);\n }\n \/\/ We don't know what our \"this\" pointer is, so just do the default\n \/\/ thing. Hopefully, we didn't need to customize the behavior yet.\n return DefWindowProc(hwnd, uMsg, wParam, lParam);\n}\n<\/pre>\n<p>\nYou pass the the <code>this<\/code> pointer to <code>Create&shy;Window<\/code>\nas the last parameter, so that the window procedure can pick it up.\n<\/p>\n<pre>\nhwnd = CreateWindow(... other parameters..., <u>this<\/u>);\n<\/pre>\n<p>\nFor dialog boxes, you can do basically the same thing.\nIt&#8217;s just that the bookkeeping is slightly different.\n<\/p>\n<ul>\n<li>\n    The <code>...Param<\/code> versions of the dialog box functions\n    are the ones which let you pass reference data.<\/p>\n<li>\n    The dialog procedure receives the reference data in the\n    <code>lParam<\/code> passed with the <code>WM_INIT&shy;DIALOG<\/code>.<\/p>\n<li>\n    The system-provided secret hiding place for dialog boxes is called\n    <code>DWLP_USER<\/code>.\n<\/ul>\n<pre>\nINT_PTR CALLBACK MyDialogClass::s_DlgProc(\n    HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)\n{\n MyDialogClass *pThis; \/\/ our \"this\" pointer will go here\n if (uMsg == WM_INITDIALOG) {\n  \/\/ Recover the \"this\" pointer which was passed as the last parameter\n  \/\/ to the ...Dialog...Param function.\n  pThis = reinterpret_cast&lt;MyDialogClass*&gt;(lParam);\n  \/\/ Put the value in a safe place for future use\n  SetWindowLongPtr(hdlg, DWLP_USER,\n                   reinterpret_cast&lt;LONG_PTR&gt;(pThis));\n } else {\n  \/\/ Recover the \"this\" pointer from where our WM_INITDIALOG handler\n  \/\/ stashed it.\n  pThis = reinterpret_cast&lt;MyDialogClass*&gt;(\n              GetWindowLongPtr(hdlg, DWLP_USER));\n }\n if (pThis) {\n  \/\/ Now that we have recovered our \"this\" pointer, let the\n  \/\/ member function finish the job.\n  return pThis-&gt;DlgProc(hwnd, uMsg, wParam, lParam);\n }\n \/\/ We don't know what our \"this\" pointer is, so just do the default\n \/\/ thing. Hopefully, we didn't need to customize the behavior yet.\n return FALSE; \/\/ returning FALSE means \"do the default thing\"\n}\n<\/pre>\n<p>\nThe above code should look really familiar, since it&#8217;s\nthe same as the window procedure case,\njust with slightly different bookkeeping.\n<\/p>\n<p>\nThe resulting classes look like this:\n<\/p>\n<pre>\nclass MyWindowClass\n{\n ... other class stuff goes here ...\n \/\/ This is the static callback that we register\n static LRESULT CALLBACK s_WndProc(\n    HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);\n \/\/ The static callback recovers the \"this\" pointer and then\n \/\/ calls this member function.\n LRESULT WndProc(\n    HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);\n};\nvoid MyWindowClass::SomeMemberFunction()\n{\n \/\/ to register the class\n WNDCLASS wc;\n ... fill out the window class as normal ...\n wc.lpfnWndProc = MyWindowClass::s_WndProc;\n wc.lpszClassName = TEXT(\"MyWindowClass\");\n RegisterClass(&amp;wc);\n \/\/ to create a window\n hwnd = CreateWindow(TEXT(\"MyWindowClass\"),\n                     ... other parameters as usual ...,\n                     <u>this<\/u>);\n}\nclass MyDialogClass\n{\n ... other class stuff goes here ...\n \/\/ This is the static callback that we register\n static INT_PTR CALLBACK s_DlgProc(\n    HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam);\n \/\/ The static callback recovers the \"this\" pointer and then\n \/\/ calls this member function.\n INT_PTR DlgProc(\n    HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam);\n};\nvoid MyDialogClass::SomeMemberFunction()\n{\n \/\/ to create the dialog box\n DialogBoxParam(...  other parameters as usual ...,\n                <u>reinterpret_cast&lt;LPARAM&gt;(this)<\/u>);\n}\n<\/pre>\n<p>\nOkay, I&#8217;ll try to write something more interesting for next week.\nBut at least I wrote this part down so I can point people at it\nin the future.\n<\/p>\n<p>\n<b>Bonus chatter<\/b>:\nAs commenter Ben noted last week,\n<a HREF=\"http:\/\/msdn.microsoft.com\/library\/ms648712\">\nDDEML\nis another component that uses the implicit reference data model<\/a>.\nIn the DDEML case, you use\n<code>Dde&shy;Set&shy;User&shy;Handle<\/code><\/a> to set the reference data\nand\n<code>Dde&shy;Query&shy;Conv&shy;Info<\/code> to retrieve it.\n<\/p>\n<p>\n(Various errors have been corrected based on comments, thanks everybody!)<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Continuing my discussion of How can I make a callback function a member of my C++ class? Common special cases for wanting to use a member function as a callback function are the window procedure and its cousin the dialog procedure. The question, then, is where to put the reference data. Let&#8217;s start with window [&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-1893","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Continuing my discussion of How can I make a callback function a member of my C++ class? Common special cases for wanting to use a member function as a callback function are the window procedure and its cousin the dialog procedure. The question, then, is where to put the reference data. Let&#8217;s start with window [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/1893","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=1893"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/1893\/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=1893"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=1893"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=1893"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}