{"id":9553,"date":"2011-09-26T07:00:00","date_gmt":"2011-09-26T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2011\/09\/26\/sending-a-window-a-wm_destroy-message-is-like-prank-calling-somebody-pretending-to-be-the-police\/"},"modified":"2011-09-26T07:00:00","modified_gmt":"2011-09-26T07:00:00","slug":"sending-a-window-a-wm_destroy-message-is-like-prank-calling-somebody-pretending-to-be-the-police","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20110926-00\/?p=9553","title":{"rendered":"Sending a window a WM_DESTROY message is like prank calling somebody pretending to be the police"},"content":{"rendered":"<p>\nA customer was trying to track down a memory leak in their program.\nTheir leak tracking tool produced the stacks which allocated memory\nthat was never freed, and they all seemed to come from\n<code>uxtheme.dll<\/code>, which is a DLL that comes with Windows.\nThe customer naturally contacted Microsoft to report what appeared\nto be a memory leak in Windows.\n<\/p>\n<p>\nI was one of the people who investigated this case,\nand the customer was able to narrow down\nthe scenario which was triggering the leak.\nEventually, I tracked it down.\nFirst, here&#8217;s the thread that caused the leak:\n<\/p>\n<pre>\nDWORD CALLBACK ThreadProc(void *lpParameter)\n{\n ...\n \/\/ This CreateWindow caused uxtheme to allocate some memory\n HWND hwnd = CreateWindow(...);\n RememberWorkerWindow(hwnd);\n MSG msg;\n while (GetMessage(&amp;msg, NULL, 0, 0)) {\n  TranslateMessage(&amp;msg);\n  DispatchMessage(&amp;msg);\n }\n return 0;\n}\n<\/pre>\n<p>\nThis thread creates an invisible window whose job is to\ndo <i>something<\/i> until it is destroyed, at which point\nthe thread is no longer needed.\nThe window procedure for the window looks like this:\n<\/p>\n<pre>\nLRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg,\n                         WPARAM wParam, LPARAM lParam)\n{\n ...\n switch (uMsg) {\n ... business logic deleted ...\n case WM_DESTROY:\n  ForgetWorkerWindow(hwnd);\n  PostQuitMessage(0);\n  break;\n ...\n }\n return DefWindowProc(hwnd, uMsg, wParam, lParam);\n}\n<\/pre>\n<p>\nSinec this is the main window on the thread,\nits destruction posts a quit message to signal the message\nloop to exit.\n<\/p>\n<p>\nThere&#8217;s nothing obviously wrong here that would cause <code>uxtheme<\/code>\nto leak memory.\nAnd yet it does.\nThe memory is allocated when the window is created,\nand it&#8217;s supposed to be freed when the window is destroyed.\nAnd the only time we exit the message loop is when the window\nis destroyed.\nSo how is it that this thread manages to exit without destroying the window?\n<\/p>\n<p>\nThe key is how the program signals this window that it should go away.\n<\/p>\n<pre>\nvoid MakeWorkerGoAway()\n{\n \/\/ Find the worker window if it is registered\n HWND hwnd = GetWorkerWindow();\n \/\/ If we have one, destroy it\n if (hwnd) {\n  \/\/ DestroyWindow doesn't work for windows that belong\n  \/\/ to other threads.\n  \/\/ DestroyWindow(hwnd);\n  SendMessage(hwnd, WM_DESTROY, 0, 0);\n }\n}\n<\/pre>\n<p>\nThe authors of this code first tried destroying the window\nwith <code>DestroyWindow<\/code> but ran into the problem that\nyou cannot destroy a window that belongs to a different thread.\n&#8220;But aha, since the <code>DestroyWindow<\/code> function sends\nthe <code>WM_DESTROY<\/code> message, we can just cut out the\nmiddle man and send the message directly.&#8221;\n<\/p>\n<p>\nWell, yeah, you can do that, but that doesn&#8217;t actually destroy\nthe window.\nIt just <i>pretends<\/i> to destroy the window by prank-calling\nthe window procedure and saying\n&#8220;Ahem, um, yeah, this is the, um, window manager?\n(stifled laughter)\nAnd, um, like, we&#8217;re just calling you to tell you, um,\nyou&#8217;re being destroyed.\n(giggle)\nSo, um, you should like pack up your bags and\n(snort)\nsell all your furniture!\n(raucous laughter)&#8221;\n<\/p>\n<p>\nThe window manager sends the <code>WM_DESTROY<\/code> message\nto a window as part of the window destruction process.\nIf you send the message yourself, then you&#8217;re making the window <i>think<\/i>\nthat it&#8217;s being destroyed, even though it isn&#8217;t.\n(Because it&#8217;s <code>DestroyWindow<\/code> that destroys windows.)\n<\/p>\n<p>\nThe victim window procedure goes through its\n&#8220;Oh dear, I&#8217;m being destroyed, I guess I&#8217;d better clean up my stuff&#8221;\nlogic, and in this case, it unregisters the worker window and\nposts a quit message to the message loop.\nThe message loop picks up the <code>WM_QUIT<\/code> and exits the thread.\n<\/p>\n<p>\nAnd that&#8217;s the memory leak:\nThe thread exited before all its windows were destroyed.\nThat worker window is still there, because it never got\n<code>DestroyWindow<\/code>&#8216;d.\nSince the window wasn&#8217;t actually destroyed, the internal memory\nused to keep track of the window didn&#8217;t get freed, and there you have\nyour leak.\n<\/p>\n<p>\n&#8220;You just got punk&#8217;d!&#8221;\n<\/p>\n<p>\nThe correct solution is for the <code>MakeWorkerGoAway<\/code>\nfunction to send a message to the worker window to tell it,\n&#8220;Hey, I&#8217;d like you to go away.\nPlease call <code>DestroyWindow<\/code> on yourself.&#8221;\nYou can invent a private message for this,\nor you can take advantage of the fact that the\ndefault behavior of the <code>WM_CLOSE<\/code> message\nis to destroy the window.\nSince our window procedure doesn&#8217;t override <code>WM_CLOSE<\/code>,\nthe message will fall through to <code>DefWindowProc<\/code>\nwhich will convert the <code>WM_CLOSE<\/code> into a <code>DestroyWindow<\/code>.\n<\/p>\n<p>\nNow that you understand the difference between destroying a window\nand prank-calling a window telling it is being destroyed,\nyou might be able to\n<a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2007\/05\/23\/407234.aspx#443420\">\nhelp Arno with his problem<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A customer was trying to track down a memory leak in their program. Their leak tracking tool produced the stacks which allocated memory that was never freed, and they all seemed to come from uxtheme.dll, which is a DLL that comes with Windows. The customer naturally contacted Microsoft to report what appeared to be a [&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-9553","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>A customer was trying to track down a memory leak in their program. Their leak tracking tool produced the stacks which allocated memory that was never freed, and they all seemed to come from uxtheme.dll, which is a DLL that comes with Windows. The customer naturally contacted Microsoft to report what appeared to be a [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/9553","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=9553"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/9553\/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=9553"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=9553"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=9553"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}