{"id":36383,"date":"2005-02-23T07:00:00","date_gmt":"2005-02-23T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2005\/02\/23\/modality-part-4-the-importance-of-setting-the-correct-owner-for-modal-ui\/"},"modified":"2005-02-23T07:00:00","modified_gmt":"2005-02-23T07:00:00","slug":"modality-part-4-the-importance-of-setting-the-correct-owner-for-modal-ui","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20050223-00\/?p=36383","title":{"rendered":"Modality, part 4: The importance of setting the correct owner for modal UI"},"content":{"rendered":"<p>\nIf you decide to display some modal UI, it is important that\nyou set the correct owner for that UI.  If you fail to heed\nthis rule, you will find yourself chasing some very strange bugs.\n<\/p>\n<p>\nLet&#8217;s return to\n<a href=\"http:\/\/blogs.msdn.com\/oldnewthing\/archive\/2003\/07\/23\/54576.aspx\">\nour scratch program<\/a> and intentionally introduce a bug\nrelated to incorrect owner windows, so that we can see the\nconsequences.\n<\/p>\n<pre>\nvoid OnChar(HWND hwnd, TCHAR ch, int cRepeat)\n{\n  switch (ch) {\n  case ' ':\n    \/\/ Wrong!\n    <i>MessageBox(NULL, TEXT(\"Message\"), TEXT(\"Title\"), MB_OK);<\/i>\n    if (!IsWindow(hwnd)) MessageBeep(-1);\n    break;\n  }\n}\n\/\/ Add to WndProc\n    HANDLE_MSG(hwnd, WM_CHAR, OnChar);\n<\/pre>\n<p>\nRun this program, press the space bar, and instead of dismissing\nthe message box, click the &#8220;X&#8221; button in the corner of the main\nwindow.  Notice that you get a beep before the program exits.\n<\/p>\n<p>\nWhat happened?\n<\/p>\n<p>\nThe beep is coming from our call to\n<a HREF=\"http:\/\/msdn.microsoft.com\/library\/en-us\/debug\/base\/messagebeep.asp\">\nthe <code>MessageBeep<\/code> function<\/a>,\nwhich in turn is telling us that our window handle is no longer valid.\nIn a real program which kept its state in per-window instance variables\n(instead of in globals like we do),\nyou would more likely crash\nbecause all the instance variables would have gone away when the\nwindow was destroyed.  In this case, the window was destroyed while\ninside a nested modal loop.  As a result, when control returned to\nthe caller, it is now a method running inside an object that has been\ndestroyed.  Any access to an instance variable is going to access\nmemory that was already freed, resulting in memory corruption or an\noutright crash.\n<\/p>\n<p>\nHere&#8217;s an explanation in a call stack diagram:\n<\/p>\n<pre>\n WinMain\n  DispatchMessage(hwnd, WM_CHAR)\n   OnChar\n    MessageBox(NULL)\n     ... modal dialog loop ...\n     DispatchMessage(hwnd, WM_CLOSE)\n      DestroyWindow(hwnd)\n       WndProc(WM_DESTROY)\n        ... clean up the window ...\n<\/pre>\n<p>\nWhen you clean up the window, you typically destroy all the\ndata structures associated with the window.  But notice that\nyou are freeing data structures <strong>that are still being used<\/strong>\nby the <code>OnChar<\/code> handler deeper in the stack.\nEventually, control unwinds back to the <code>OnChar<\/code>,\nwhich is now running with an invalid instance pointer.\n(If you believe in C++ objects, you would find that its &#8220;this&#8221;\npointer has gone invalid.)\n<\/p>\n<p>\nThis was caused by\n<a HREF=\"http:\/\/groups-beta.google.com\/group\/comp.os.ms-windows.programmer.win32\/msg\/850b8e9e80c4cbae\">\nfailing to set the correct owner for the\nmodal <code>MessageBox<\/code> call<\/a>, allowing the user to\ninteract with the frame window at a time when the frame window\nisn&#8217;t expecting to have its state changed.\n<\/p>\n<p>\nEven more problematic, the user can switch back to the frame\nwindow and hit the space bar again.  The result: Another\nmessage box.  Repeat another time and you end up with a stack that\nlooks like this:\n<\/p>\n<pre>\n WinMain\n  DispatchMessage(hwnd, WM_CHAR)\n   OnChar\n    MessageBox(NULL)\n     ... modal dialog loop ...\n     DispatchMessage(hwnd, WM_CHAR)\n       OnChar\n\tMessageBox(NULL)\n\t ... modal dialog loop ...\n\t DispatchMessage(hwnd, WM_CHAR)\n\t   OnChar\n\t    MessageBox(NULL)\n\t     ... modal dialog loop ...\n<\/pre>\n<p>\nThere are now four top-level windows, all active.  If the user\ndismisses them in any order other than the reverse order in\nwhich they were created, you&#8217;re going to have a problem on your\nhands.  For example, if the user dismisses the second message box\nfirst, the part of the stack corresponding to that nesting level\nwill end up returning to a destroyed window when the third message\nbox is finally dismissed.\n<\/p>\n<p>\nThe fix is simple, and we&#8217;ll pick up there next time.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>If you decide to display some modal UI, it is important that you set the correct owner for that UI. If you fail to heed this rule, you will find yourself chasing some very strange bugs. Let&#8217;s return to our scratch program and intentionally introduce a bug related to incorrect owner windows, so that we [&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,143],"class_list":["post-36383","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code","tag-modality"],"acf":[],"blog_post_summary":"<p>If you decide to display some modal UI, it is important that you set the correct owner for that UI. If you fail to heed this rule, you will find yourself chasing some very strange bugs. Let&#8217;s return to our scratch program and intentionally introduce a bug related to incorrect owner windows, so that we [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/36383","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=36383"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/36383\/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=36383"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=36383"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=36383"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}