user32: better fix how to handle GetWindowLongPtr[AW](..., GWLP_WNDPROC) for builtin winprocs (with tests, fixes bug #$9198) (resend)

Mikolaj Zalewski mikolajz at google.com
Wed Sep 12 12:35:26 CDT 2007


In the previous patch I chose the edit control to test but this
control is an exception - unlike all the other built-in controls, for
e.g. GetWindowLongPtrA(hwnd, GWLP_WNDPROC) on a Unicode control it
returns a handle to a winproc, not a pointer. That's why that return
value can be called with CallWindowProcW and it works. For all the
other controls both GetWindowLongPtrA and W would return pointers and
CallWindowProcA and W would behave the same calling the function
directly. The fact that for an edit control we have no W->A->W
conversion if we do CallWindowProcW on GetWindowLongPtrA suggests that
the control have a normal builtin winproc with both A and W functions
and the hack is on the level of GetWindowLongPtr.

This fixes a bug in Photoshop 6 where an edit control is subclassed
twice, once as Unicode and the second time as ANSI.
-------------- next part --------------
From 50eacd2465df53e5cfc15458ff5ab80f0cc07e7e Mon Sep 17 00:00:00 2001
From: Mikolaj Zalewski <mikolaj at zalewski.pl>
Date: Mon, 10 Sep 2007 14:03:49 -0700
Subject: [PATCH] user32: better fix how to handle GetWindowLongPtr[AW](..., GWLP_WNDPROC) for builtin winprocs
---
 dlls/user32/class.c       |    6 +++++-
 dlls/user32/controls.h    |    2 ++
 dlls/user32/edit.c        |    2 ++
 dlls/user32/tests/class.c |   47 +++++++++++++++++++++++++++++++++++++++++----
 dlls/user32/win.c         |   16 ++++++++++++++-
 dlls/user32/winproc.c     |   41 ++++-----------------------------------
 6 files changed, 71 insertions(+), 43 deletions(-)

diff --git a/dlls/user32/class.c b/dlls/user32/class.c
index 6be4cad..e5dd701 100644
--- a/dlls/user32/class.c
+++ b/dlls/user32/class.c
@@ -402,12 +402,13 @@ void CLASS_RegisterBuiltinClasses(void)
     extern const struct builtin_class_descr SCROLL_builtin_class;
     extern const struct builtin_class_descr STATIC_builtin_class;
 
+    CLASS *edit_class;
+
     register_builtin( &DESKTOP_builtin_class );
     register_builtin( &BUTTON_builtin_class );
     register_builtin( &COMBO_builtin_class );
     register_builtin( &COMBOLBOX_builtin_class );
     register_builtin( &DIALOG_builtin_class );
-    register_builtin( &EDIT_builtin_class );
     register_builtin( &ICONTITLE_builtin_class );
     register_builtin( &LISTBOX_builtin_class );
     register_builtin( &MDICLIENT_builtin_class );
@@ -415,6 +416,9 @@ void CLASS_RegisterBuiltinClasses(void)
     register_builtin( &SCROLL_builtin_class );
     register_builtin( &STATIC_builtin_class );
 
+    edit_class = register_builtin( &EDIT_builtin_class );
+    EDIT_winproc_handle = edit_class->winproc;
+
     /* the DefWindowProc winprocs are magic too */
     WINPROC_AllocProc( DefWindowProcA, DefWindowProcW );
 }
diff --git a/dlls/user32/controls.h b/dlls/user32/controls.h
index 6178100..594b665 100644
--- a/dlls/user32/controls.h
+++ b/dlls/user32/controls.h
@@ -43,6 +43,8 @@ struct builtin_class_descr
     HBRUSH    brush;   /* brush or system color */
 };
 
+extern WNDPROC EDIT_winproc_handle;
+
 /* Class functions */
 struct tagCLASS;  /* opaque structure */
 struct tagWND;
diff --git a/dlls/user32/edit.c b/dlls/user32/edit.c
index 77ecf32..6aaf149 100644
--- a/dlls/user32/edit.c
+++ b/dlls/user32/edit.c
@@ -293,6 +293,8 @@ static void EDIT_ImeComposition(HWND hwn
 LRESULT WINAPI EditWndProcA(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
 LRESULT WINAPI EditWndProcW(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
 
+WNDPROC EDIT_winproc_handle;  /* filled after registering the classes in classes.c */
+
 /*********************************************************************
  * edit class descriptor
  */
diff --git a/dlls/user32/tests/class.c b/dlls/user32/tests/class.c
index 7f54e2f..6acf93d 100644
--- a/dlls/user32/tests/class.c
+++ b/dlls/user32/tests/class.c
@@ -39,6 +39,9 @@ static const WCHAR WC_EDITW[] = {'E','d'
 
 #define NUMCLASSWORDS 4
 
+/* we may need to update it for Win64 */
+#define IS_WNDPROC_HANDLE(x) (((LONG_PTR)x & 0xffff0000) == 0xffff0000)
+
 static LRESULT WINAPI ClassTest_WndProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
 {
     return DefWindowProcW (hWnd, msg, wParam, lParam);
@@ -565,8 +568,16 @@ static void test_instances(void)
     check_thread_instance( "EDIT", (HINSTANCE)0x12345678, (HINSTANCE)0x12345678, (HINSTANCE)0xdeadbeef );
 }
 
-static void test_defwndproc(void)
+static void test_builtinproc(void)
 {
+    /* Edit behaves differently. ScrollBar have currently only a Unicode winproc */
+    static const CHAR NORMAL_CLASSES[][40] = {
+        "Button",
+        "Static",
+        "ComboBox",
+        "ListBox",
+    };
+    static const int NUM_NORMAL_CLASSES = (sizeof(NORMAL_CLASSES)/sizeof(NORMAL_CLASSES[0]));
     static const char classA[] = "deftest";
     static const WCHAR classW[] = {'d','e','f','t','e','s','t',0};
     WCHAR unistring[] = {0x142, 0x40e, 0x3b4, 0};  /* a string that would be destoryed by a W->A->W conversion */
@@ -662,10 +673,35 @@ static void test_defwndproc(void)
     DestroyWindow(hwnd);
     UnregisterClass((LPSTR)(DWORD_PTR)atom, GetModuleHandle(NULL));
 
-    /* calling built-in and custom winprocs with CallWindowProc[AW]. Despite
-     * a slightly different nature the end result is the same */
+    /* For most of the builtin controls both GetWindowLongPtrA and W returns a pointer that is executed directly
+     * by CallWindowProcA/W */
+    for (i = 0; i < NUM_NORMAL_CLASSES; i++)
+    {
+        WNDPROC procA, procW;
+        hwnd = CreateWindowExA(0, NORMAL_CLASSES[i], classA, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 680, 260,
+            NULL, NULL, NULL, 0);
+        ok(hwnd != NULL, "Couldn't create window of class %s\n", NORMAL_CLASSES[i]);
+        SetWindowText(hwnd, classA);  /* ComboBox needs this */
+        procA = (WNDPROC)GetWindowLongPtrA(hwnd, GWLP_WNDPROC);
+        procW = (WNDPROC)GetWindowLongPtrW(hwnd, GWLP_WNDPROC);
+        ok(!IS_WNDPROC_HANDLE(procA), "procA should not be a handle for %s (%p)\n", NORMAL_CLASSES[i], procA);
+        ok(!IS_WNDPROC_HANDLE(procW), "procW should not be a handle for %s (%p)\n", NORMAL_CLASSES[i], procW);
+        CallWindowProcA(procA, hwnd, WM_GETTEXT, 120, (LPARAM)buf);
+        ok(memcmp(buf, classA, sizeof(classA)) == 0, "WM_GETTEXT A/A invalid return for class %s\n", NORMAL_CLASSES[i]);
+        CallWindowProcA(procW, hwnd, WM_GETTEXT, 120, (LPARAM)buf);
+        ok(memcmp(buf, classW, sizeof(classW)) == 0, "WM_GETTEXT A/W invalid return for class %s\n", NORMAL_CLASSES[i]);
+        CallWindowProcW(procA, hwnd, WM_GETTEXT, 120, (LPARAM)buf);
+        ok(memcmp(buf, classA, sizeof(classA)) == 0, "WM_GETTEXT W/A invalid return for class %s\n", NORMAL_CLASSES[i]);
+        CallWindowProcW(procW, hwnd, WM_GETTEXT, 120, (LPARAM)buf);
+        ok(memcmp(buf, classW, sizeof(classW)) == 0, "WM_GETTEXT W/W invalid return for class %s\n", NORMAL_CLASSES[i]);
+        DestroyWindow(hwnd);
+    }
+
+    /* Edit controls are special - they return a wndproc handle when GetWindowLongPtr is called with a different A/W.
+     * On the other hand there is no W->A->W conversion so this control is threated specially. */
     hwnd = CreateWindowW(WC_EDITW, unistring, WS_OVERLAPPEDWINDOW,
       CW_USEDEFAULT, CW_USEDEFAULT, 680, 260, NULL, NULL, NULL, 0);
+    ok(IS_WNDPROC_HANDLE(GetWindowLongPtrA(hwnd, GWLP_WNDPROC)), "Edit control should return a wndproc handle\n");
     CallWindowProcW((WNDPROC)GetWindowLongPtrW(hwnd, GWLP_WNDPROC), hwnd, WM_GETTEXT, 120, (LPARAM)buf);
     ok(memcmp(buf, unistring, sizeof(unistring)) == 0, "WM_GETTEXT invalid return\n");
     CallWindowProcA((WNDPROC)GetWindowLongPtrW(hwnd, GWLP_WNDPROC), hwnd, WM_GETTEXT, 120, (LPARAM)buf);
@@ -696,6 +732,7 @@ static void test_defwndproc(void)
     hwnd = CreateWindowA(WC_EDITA, classA, WS_OVERLAPPEDWINDOW,
       CW_USEDEFAULT, CW_USEDEFAULT, 680, 260, NULL, NULL, NULL, 0);
 
+    ok(IS_WNDPROC_HANDLE(GetWindowLongPtrW(hwnd, GWLP_WNDPROC)), "Edit control should return a wndproc handle\n");
     CallWindowProcA((WNDPROC)GetWindowLongPtrA(hwnd, GWLP_WNDPROC), hwnd, WM_GETTEXT, 120, (LPARAM)buf);
     ok(memcmp(buf, classA, sizeof(classA)) == 0, "WM_GETTEXT invalid return\n");
     CallWindowProcA((WNDPROC)GetWindowLongPtrW(hwnd, GWLP_WNDPROC), hwnd, WM_GETTEXT, 120, (LPARAM)buf);
@@ -785,6 +822,8 @@ START_TEST(class)
     ClassTest(hInstance,TRUE);
     CreateDialogParamTest(hInstance);
     test_styles();
+    test_builtinproc();
+    
+    /* this test unregisters the Button class so it should be executed at the end */
     test_instances();
-    test_defwndproc();
 }
diff --git a/dlls/user32/win.c b/dlls/user32/win.c
index 41eb108..2ae3161 100644
--- a/dlls/user32/win.c
+++ b/dlls/user32/win.c
@@ -1788,7 +1788,21 @@ static LONG_PTR WIN_GetWindowLong( HWND 
     case GWL_STYLE:      retvalue = wndPtr->dwStyle; break;
     case GWL_EXSTYLE:    retvalue = wndPtr->dwExStyle; break;
     case GWLP_ID:        retvalue = (ULONG_PTR)wndPtr->wIDmenu; break;
-    case GWLP_WNDPROC:   retvalue = (ULONG_PTR)WINPROC_GetProc( wndPtr->winproc, unicode ); break;
+    case GWLP_WNDPROC:   
+        {
+            /* This looks like a hack only for the edit control (see tests). This makes these controls
+             * more tolerant to A/W mismatches. The lack of W->A->W conversion for such a mismatch suggests
+             * that the hack is in GetWindowLongPtr[AW], not in winprocs.
+             */
+            if (wndPtr->winproc == EDIT_winproc_handle)
+                if ((unicode && !(wndPtr->flags & WIN_ISUNICODE)) || (!unicode && (wndPtr->flags & WIN_ISUNICODE)))
+                {
+                    retvalue = (ULONG_PTR)wndPtr->winproc;
+                    break;
+                }
+            retvalue = (ULONG_PTR)WINPROC_GetProc( wndPtr->winproc, unicode );
+            break;
+        }
     case GWLP_HINSTANCE: retvalue = (ULONG_PTR)wndPtr->hInstance; break;
     default:
         WARN("Unknown offset %d\n", offset );
diff --git a/dlls/user32/winproc.c b/dlls/user32/winproc.c
index e58d1f0..51e5112 100644
--- a/dlls/user32/winproc.c
+++ b/dlls/user32/winproc.c
@@ -113,19 +113,6 @@ static inline WINDOWPROC *find_winproc( 
     return NULL;
 }
 
-/* find an existing builtin winproc */
-static inline WINDOWPROC *find_builtin_proc( WNDPROC func )
-{
-    unsigned int i;
-
-    for (i = 0; i < builtin_used; i++)
-    {
-        if (winproc_array[i].procA == func || winproc_array[i].procW == func)
-            return &winproc_array[i];
-    }
-    return NULL;
-}
-
 /* return the window proc for a given handle, or NULL for an invalid handle */
 static inline WINDOWPROC *handle_to_proc( WNDPROC handle )
 {
@@ -2288,12 +2275,7 @@ LRESULT WINAPI CallWindowProcA(
     if (!func) return 0;
 
     if (!(proc = handle_to_proc( func )))
-    {
-        if ((proc = find_builtin_proc( func )) && !IsWindowUnicode( hwnd ))
-            call_window_proc( hwnd, msg, wParam, lParam, &result, proc->procA );
-        else
-            call_window_proc( hwnd, msg, wParam, lParam, &result, func );
-    }
+        call_window_proc( hwnd, msg, wParam, lParam, &result, func );
     else if (proc->procA)
         call_window_proc( hwnd, msg, wParam, lParam, &result, proc->procA );
     else if (proc->procW)
@@ -2319,12 +2301,7 @@ LRESULT WINAPI CallWindowProcW( WNDPROC 
     if (!func) return 0;
 
     if (!(proc = handle_to_proc( func )))
-    {
-        if ((proc = find_builtin_proc( func )) && IsWindowUnicode( hwnd ))
-            call_window_proc( hwnd, msg, wParam, lParam, &result, proc->procW );
-        else
-            call_window_proc( hwnd, msg, wParam, lParam, &result, func );
-    }
+        call_window_proc( hwnd, msg, wParam, lParam, &result, func );
     else if (proc->procW)
         call_window_proc( hwnd, msg, wParam, lParam, &result, proc->procW );
     else if (proc->procA)
@@ -2382,12 +2359,7 @@ INT_PTR WINPROC_CallDlgProcA( DLGPROC fu
     if (!func) return 0;
 
     if (!(proc = handle_to_proc( func )))
-    {
-        if ((proc = find_builtin_proc( func )) && !IsWindowUnicode( hwnd ))
-            ret = call_dialog_proc( hwnd, msg, wParam, lParam, &result, proc->procA );
-        else
-            ret = call_dialog_proc( hwnd, msg, wParam, lParam, &result, func );
-    }
+        ret = call_dialog_proc( hwnd, msg, wParam, lParam, &result, func );
     else if (proc->procA)
         ret = call_dialog_proc( hwnd, msg, wParam, lParam, &result, proc->procA );
     else if (proc->procW)
@@ -2417,12 +2389,7 @@ INT_PTR WINPROC_CallDlgProcW( DLGPROC fu
     if (!func) return 0;
 
     if (!(proc = handle_to_proc( func )))
-    {
-        if ((proc = find_builtin_proc( func )) && IsWindowUnicode( hwnd ))
-            ret = call_dialog_proc( hwnd, msg, wParam, lParam, &result, proc->procW );
-        else
-            ret = call_dialog_proc( hwnd, msg, wParam, lParam, &result, func );
-    }
+        ret = call_dialog_proc( hwnd, msg, wParam, lParam, &result, func );
     else if (proc->procW)
         ret = call_dialog_proc( hwnd, msg, wParam, lParam, &result, proc->procW );
     else if (proc->procA)
-- 
1.4.1


More information about the wine-patches mailing list