{"id":9323,"date":"2011-10-21T07:00:00","date_gmt":"2011-10-21T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2011\/10\/21\/the-psn_setactive-notification-is-sent-each-time-your-wizard-page-is-activated\/"},"modified":"2011-10-21T07:00:00","modified_gmt":"2011-10-21T07:00:00","slug":"the-psn_setactive-notification-is-sent-each-time-your-wizard-page-is-activated","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20111021-00\/?p=9323","title":{"rendered":"The PSN_SETACTIVE notification is sent each time your wizard page is activated"},"content":{"rendered":"<p>\nA customer had received a number of crashes via\n<a HREF=\"http:\/\/www.microsoft.com\/whdc\/maintain\/StartWER.mspx\">\nWindows Error Reporting<\/a>\nand believed that they had found a bug in the tree view common control.\n<\/p>\n<blockquote CLASS=\"q\">\n<p>\nIn our UI, we have a tree view with checkboxes.\nThe tree view displays a fixed item at the top,\nfollowed by a variable number of dynamic items.\nWhen the user clicks <i>Next<\/i>,\nwe look at the tree view to determine what the user selected.\nThe code goes like this (pseudo):\n<\/p>\n<pre>\nhtiRoot = GetTreeRoot();\n\/\/ First process the fixed item\nhtiFixed = GetChild(htiRoot);\nif (IsTreeViewItemChecked(htiFixed)) {\n    .. add the fixed item ...\n}\n\/\/ Now process the dynamic items\nhti = GetNextSibling(htiFixed);\nwhile (hti != NULL) {\n  if (IsTreeViewItemChecked(hti)) {\n    ... add the dynamic item ...\n  }\n  hti = GetNextSibling(hti);\n}\n<\/pre>\n<p>\nIn the crashes we receive, other variables in the\nprogram indicate that there should be only one dynamic item,\nbut our loop iterates multiple times.\nFurthermore, the first time through the loop,\nthe <code>hItem<\/code> is not the handle to the first\ndynamic item but is in fact the handle to the fixed item.\nThis naturally results in a crash when we try to treat the fixed item\nas if it were a dynamic item.\n<\/p>\n<p>\nAnother thing we noticed is that at the time of the crash,\nall three variables <code>htiRoot<\/code>\n<code>htiFixed<\/code>,\nand\n<code>hti<\/code> have the same value.\n<\/p>\n<p>\nOur attempts to reproduce the problem in-house have been\nunsuccessful.\nFrom our analysis, we believe that the tree view APIs used to\nobtain handles to children and sibling nodes are misbehaving.\n<\/p>\n<\/blockquote>\n<p>\nThe customer included the crash bucket number,\nso we were able to connect to the same crash dumps that the customer\nwas looking at.\n<\/p>\n<p>\nThe first thing to dismiss was the remark that all three of\nthe local variables had the same value.\nThis is to be expected since they have non-overlapping lifetimes,\nand the compiler decided to alias them all to each other to save\nmemory.\n<\/p>\n<pre>\n...\n        lea     eax,[ebp+8]         ; htiRoot\n        push    eax\n        push    1                   ; some flag\n        push    ebx                 ; some parameter\n        call    00965fb9            ; GetTreeRoot\n        mov     [ebp-2Ch],eax\n        test    eax, eax\n        jl      00971a49            ; failed - exit\n        mov     edi, [_imp__SendMessageW]\n        push    4                   ; TVGN_CHILD\n        push    110Ah               ; TVM_GETNEXTITEM\n        push    dword ptr [ebx+10h] ; window handle\n        call    edi                 ; SendMessage\n        mov     [ebp+8],eax         ; htiFixed\n    ... eliding if (IsTreeViewItemChecked(...)) ...\n        jmp     00971a1c            ; enter loop\n00971931:\n    ... eliding if (IsTreeViewItemChecked(...)) ...\n00971a1c:\n        push    dword ptr [ebp+8]   ; hti\n        push    1                   ; TVGN_NEXT\n        push    110Ah               ; TVM_GETNEXTITEM\n        push    dword ptr [ebx+10h] ; window handle\n        call    edi                 ; SendMessage\n        mov     [ebp+8],eax         ; update hti\n        test    eax, eax            ; hti == NULL?\n        jne     00971931            ; N: continue loop\n<\/pre>\n<p>\nI&#8217;ve removed code not directly relevant to the discussion.\nThe point to see here is that the compiler combined all three\nvariables into one physical memory location at\n<code>[ebp+8]<\/code>\nsince there is no\npoint in the program where more than one of the values is needed\nat a time.\nIn other words, the compiler rewrote your code like this:\n<\/p>\n<pre>\nhti = GetTreeRoot();\nhti = GetChild(hti);\nif (IsTreeViewItemChecked(hti)) {\n    .. add the fixed item ...\n}\nwhile ((hti = GetNextSibling(hti)) != NULL) {\n  if (IsTreeViewItemChecked(hti)) {\n    ... add the dynamic item ...\n  }\n}\n<\/pre>\n<p>\nNot only did the compiler merge all your <code>hti<\/code>\nvariables into one, it realized that once it did that,\nthe two calls to <code>Get&shy;Next&shy;Sibling<\/code>\ncould be folded together as well.\n<\/p>\n<p>\nOkay, one mystery solved.\nWhat about the others?\n<\/p>\n<p>\nFrom studying the crash dump, the shell team determined that\nthe reason the first dynamic item appears to be the fixed item\nis that the tree view actually has <i>two<\/i> fixed items:\n<\/p>\n<pre>\n003d06d8 Root\n+ 003d0a38 \"Configuration settings\"\n+ 003d0888 \"Configuration settings\"\n+ 003d07b0 \"Saved game from May 27, 2009 at 2:42 PM (playing as Thor)\"\n+ 003d0600 \"Saved game from May 27, 2009 at 2:42 PM (playing as Thor)\"\n<\/pre>\n<p>\n&#8220;Configuration settings&#8221; is the fixed item, and the saved\ngames are the dynamic items.\n(This isn&#8217;t the actual scenario from the customer, but it\ngets the point across.)\nThe customer was wrong to use the definite article when referring\nto <i>the<\/i> handle to\n<i>the<\/i> fixed item, since there are two fixed items here.\nIn a sense, the customer&#8217;s understanding that there is only\none fixed item clouded their ability to debug the problem:\nWhen they saw another fixed item, they assumed not that they\nreceived another item that was fixed,\nbut rather that they were getting the same fixed item twice.\n<\/p>\n<p>\nSeeing that the tree view was being populated twice\ndirected the next step of the investigation: Why?\n<\/p>\n<p>\nThe code that populates the tree view is called from\nthe wizard page&#8217;s <code>PSN_SET&shy;ACTIVE<\/code> notification,\nand that one piece of information was the last piece of the\npuzzle.\n<\/p>\n<p>\nThe <code>PSN_SET&shy;ACTIVE<\/code> notification is sent each time\nthe wizard or property sheet page is selected as the current page.\nIf the page is activated twice, then you will get two\n<code>PSN_SET&shy;ACTIVE<\/code> notifications.\nThe solution was to populate the tree view only the first\ntime the page was activated.\n<\/p>\n<p>\n<b>Exercise<\/b>: What was missing from the customer&#8217;s testing\nthat prevented them from reproducing the problem in their labs?<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A customer had received a number of crashes via Windows Error Reporting and believed that they had found a bug in the tree view common control. In our UI, we have a tree view with checkboxes. The tree view displays a fixed item at the top, followed by a variable number of dynamic items. When [&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-9323","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>A customer had received a number of crashes via Windows Error Reporting and believed that they had found a bug in the tree view common control. In our UI, we have a tree view with checkboxes. The tree view displays a fixed item at the top, followed by a variable number of dynamic items. When [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/9323","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=9323"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/9323\/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=9323"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=9323"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=9323"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}