]> asedeno.scripts.mit.edu Git - PuTTY.git/commitdiff
Fix accidental dependence on Windows API quirk in config box.
authorSimon Tatham <anakin@pobox.com>
Thu, 18 Jun 2015 06:05:19 +0000 (07:05 +0100)
committerSimon Tatham <anakin@pobox.com>
Sat, 20 Jun 2015 11:47:43 +0000 (12:47 +0100)
Our config boxes are constructed using the CreateDialog() API
function, rather than the modal DialogBox(). CreateDialog() is not
that different from CreateWindow(), so windows created with it don't
appear on the screen automatically; MSDN says that they must be shown
via ShowWindow(), just like non-dialog windows have to be. But we
weren't doing that at any point!

So how was our config box ever getting displayed at all? Apparently by
sheer chance, it turns out. The handler for a selection change in the
tree view, which has to delete a whole panel of controls and creates a
different set, surrounds that procedure with some WM_SETREDRAW calls
and an InvalidateRect(), to prevent flicker while lots of changes were
being made. And the creation of the _first_ panelful of controls, at
dialog box setup, was done by simply selecting an item in the treeview
and expecting that handler to be recursively called. And it appears
that calling WM_SETREDRAW(TRUE) and then InvalidateRect was
undocumentedly having an effect equivalent to the ShowWindow() we
should have called, so that we never noticed the latter was missing.

But a recent Vista update (all reports implicate KB3057839) has caused
that not to work any more: on an updated Vista machine, in some
desktop configurations, it seems that any attempt to fiddle with
WM_SETREDRAW during dialog setup can leave the dialog box in a really
unhelpful invisible state - the window is _physically there_ (you can
see its taskbar entry, and the mouse pointer changes as you move over
where its edit boxes are), but 100% transparent.

So now we're doing something a bit more sensible. The first panelful
of controls is created directly by the WM_INITDIALOG handler, rather
than recursing into code that wasn't really designed to run at setup
time. To be on the safe side, that handler for treeview selection
change is also disabled until the WM_INITDIALOG handler has finished
(like we already did with the WM_COMMAND handler), so that we can be
sure of not accidentally messing about with WM_SETREDRAW at all during
setup. And at the end of setup, we show the window in the sensible
way, by a docs-approved call to ShowWindow().

This appears (on the one machine I've so far tested it on) to fix the
Vista invisible-window issue, and also it should be more API-compliant
and hence safer in future.

(cherry picked from commit 6163710f043fb58fc80f6b45c14a92f7036bde75)

windows/windlg.c

index aadf88ea5fb68fc096e6f3465e18d5b5529dff32..716d046b021aff50b3a8373210bf56152db70e3e 100644 (file)
@@ -454,6 +454,7 @@ static int CALLBACK GenericMainDlgProc(HWND hwnd, UINT msg,
            HTREEITEM hfirst = NULL;
            int i;
            char *path = NULL;
+            char *firstpath = NULL;
 
            for (i = 0; i < ctrlbox->nctrlsets; i++) {
                struct controlset *s = ctrlbox->ctrlsets[i];
@@ -486,18 +487,26 @@ static int CALLBACK GenericMainDlgProc(HWND hwnd, UINT msg,
                        c++;
 
                item = treeview_insert(&tvfaff, j, c, s->pathname);
-               if (!hfirst)
+               if (!hfirst) {
                    hfirst = item;
+                    firstpath = s->pathname;
+                }
 
                path = s->pathname;
            }
 
            /*
-            * Put the treeview selection on to the Session panel.
-            * This should also cause creation of the relevant
-            * controls.
+            * Put the treeview selection on to the first panel in the
+            * ctrlbox.
             */
            TreeView_SelectItem(treeview, hfirst);
+
+            /*
+             * And create the actual control set for that panel, to
+             * match the initial treeview selection.
+             */
+            create_controls(hwnd, firstpath);
+           dlg_refresh(NULL, &dp);    /* and set up control values */
        }
 
        /*
@@ -516,6 +525,18 @@ static int CALLBACK GenericMainDlgProc(HWND hwnd, UINT msg,
            }
        }
 
+        /*
+         * Now we've finished creating our initial set of controls,
+         * it's safe to actually show the window without risking setup
+         * flicker.
+         */
+        ShowWindow(hwnd, SW_SHOWNORMAL);
+
+        /*
+         * Set the flag that activates a couple of the other message
+         * handlers below, which were disabled until now to avoid
+         * spurious firing during the above setup procedure.
+         */
        SetWindowLongPtr(hwnd, GWLP_USERDATA, 1);
        return 0;
       case WM_LBUTTONUP:
@@ -530,10 +551,21 @@ static int CALLBACK GenericMainDlgProc(HWND hwnd, UINT msg,
       case WM_NOTIFY:
        if (LOWORD(wParam) == IDCX_TREEVIEW &&
            ((LPNMHDR) lParam)->code == TVN_SELCHANGED) {
-           HTREEITEM i =
-               TreeView_GetSelection(((LPNMHDR) lParam)->hwndFrom);
+            /*
+             * Selection-change events on the treeview cause us to do
+             * a flurry of control deletion and creation - but only
+             * after WM_INITDIALOG has finished. The initial
+             * selection-change event(s) during treeview setup are
+             * ignored.
+             */
+           HTREEITEM i;
            TVITEM item;
            char buffer[64];
+
+            if (GetWindowLongPtr(hwnd, GWLP_USERDATA) != 1)
+                return 0;
+
+            i = TreeView_GetSelection(((LPNMHDR) lParam)->hwndFrom);
  
            SendMessage (hwnd, WM_SETREDRAW, FALSE, 0);