user32: better fix how to handle GetWindowLongPtr[AW](...,
GWLP_WNDPROC) for builtin winprocs (with tests)
Mikolaj Zalewski
mikolajz at google.com
Mon Sep 10 16:16:13 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 a pointer 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