{"id":102992,"date":"2019-10-14T07:00:00","date_gmt":"2019-10-14T14:00:00","guid":{"rendered":"http:\/\/devblogs.microsoft.com\/oldnewthing\/?p=102992"},"modified":"2019-10-13T16:03:09","modified_gmt":"2019-10-13T23:03:09","slug":"20191014-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20191014-00\/?p=102992","title":{"rendered":"A common mistake when you try to create a C++ class that wraps a window procedure: Saving the window handle too late"},"content":{"rendered":"<p>A common mistake when you try to create a C++ class that wraps a window procedure is saving the window handle too late.<\/p>\n<pre>\/\/ Code in italics is wrong.\r\nclass MyWindowClass\r\n{\r\nprivate:\r\n HWND m_hwnd = nullptr;\r\n\r\n static LRESULT CALLBACK StaticWndProc(\r\n    HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)\r\n {\r\n  MyWindowClass *self;\r\n  if (uMsg == WM_NCCREATE) {\r\n   LPCREATESTRUCT lpcs = reinterpret_cast&lt;LPCREATESTRUCT&gt;(lParam);\r\n   self = static_cast&lt;MyWindowClass*&gt;(lpcs-&gt;lpCreateParams);\r\n   SetWindowLongPtr(hwnd, GWLP_USERDATA,\r\n                    reinterpret_cast&lt;LONG_PTR&gt;(self));\r\n  } else {\r\n   self = reinterpret_cast&lt;MyWindowClass*&gt;(\r\n               GetWindowLongPtr(hwnd, GWLP_USERDATA));\r\n  }\r\n  if (self) {\r\n   return self-&gt;WndProc(uMsg, wParam, lParam);\r\n  }\r\n  return DefWindowProc(hwnd, uMsg, wParam, lParam);\r\n }\r\n\r\n LRESULT WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam)\r\n {\r\n   switch (uMsg) {\r\n   ...\r\n   default:\r\n    return DefWindowProc(m_hwnd, uMsg, wParam, lParam);\r\n   }\r\n }\r\n\r\npublic:\r\n void CreateTheWindow()\r\n {\r\n   ... RegisterClass() ...\r\n   <i>m_hwnd = CreateWindowEx(..., this);<\/i>\r\n }\r\n};\r\n<\/pre>\n<p>This code follows the usual pattern for a window procedure wrapper: The <code>this<\/code> pointer is passed as the creation parameter, and the <code>WM_<\/code><code>NC\u00adCREATE<\/code> message handler stashes the creation parameter in the window extra bytes, thereby allowing the <code>this<\/code> pointer to be recovered from the window handle when handling future messages.<\/p>\n<p>However, there&#8217;s a problem with the above code. Can you spot it?<\/p>\n<p>The problem is that it sets the <code>m_hwnd<\/code> member variable too late.<\/p>\n<p>As written, the code doesn&#8217;t set the <code>m_hwnd<\/code> member variable until the <code>Create\u00adWindow\u00adEx<\/code> function returns. But creating a window involves sending many messages.<\/p>\n<p>For every message received during window creation, The <code>WndProc<\/code> member function runs with a null <code>m_hwnd<\/code>. This means that when it calls <code>Def\u00adWindow\u00adProc(m_hwnd, ...)<\/code>, it&#8217;s passing an invalid parameter.<\/p>\n<p>Many of the messages sent during window creation are kind of important to pass through to <code>Def\u00adWindow\u00adProc<\/code>. For example, if you neglect to pass <code>WM_<\/code><code>NC\u00adCREATE<\/code> to <code>Def\u00adWindow\u00adProc<\/code>, your window will not be properly initialized.<\/p>\n<p>The solution is to set <code>m_hwnd<\/code> as soon as you learn what the window handle is.<\/p>\n<pre>  if (uMsg == WM_NCCREATE) {\r\n   LPCREATESTRUCT lpcs = reinterpret_cast&lt;LPCREATESTRUCT&gt;(lParam);\r\n   self = static_cast&lt;MyWindowClass*&gt;(lpcs-&gt;lpCreateParams);\r\n   <span style=\"color: blue;\">self-&gt;m_hwnd = hwnd; \/\/ save the window handle too!<\/span>\r\n   SetWindowLongPtr(hwnd, GWLP_USERDATA,\r\n                    reinterpret_cast&lt;LONG_PTR&gt;(self));\r\n  } \r\n<\/pre>\n<p>Don&#8217;t wait for <code>Create\u00adWindow\u00adEx<\/code> to return. By then, it&#8217;s too late.<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>You need it before then.<\/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-102992","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>You need it before then.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/102992","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=102992"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/102992\/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=102992"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=102992"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=102992"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}