diff --git a/dlls/user32/class.c b/dlls/user32/class.c index 6be4cadd3cb..a0be005efb8 100644 --- a/dlls/user32/class.c +++ b/dlls/user32/class.c @@ -366,7 +366,7 @@ static CLASS *CLASS_RegisterClass( ATOM atom, HINSTANCE hInstance, BOOL local, * Register a builtin control class. * This allows having both ASCII and Unicode winprocs for the same class. */ -static CLASS *register_builtin( const struct builtin_class_descr *descr ) +static WNDPROC register_builtin( const struct builtin_class_descr *descr ) { ATOM atom; CLASS *classPtr; @@ -380,7 +380,7 @@ static CLASS *register_builtin( const struct builtin_class_descr *descr ) classPtr->hbrBackground = descr->brush; classPtr->winproc = WINPROC_AllocProc( descr->procA, descr->procW ); release_class_ptr( classPtr ); - return classPtr; + return classPtr->winproc; } @@ -407,7 +407,7 @@ void CLASS_RegisterBuiltinClasses(void) register_builtin( &COMBO_builtin_class ); register_builtin( &COMBOLBOX_builtin_class ); register_builtin( &DIALOG_builtin_class ); - register_builtin( &EDIT_builtin_class ); + EDIT_winproc_handle = register_builtin( &EDIT_builtin_class ); register_builtin( &ICONTITLE_builtin_class ); register_builtin( &LISTBOX_builtin_class ); register_builtin( &MDICLIENT_builtin_class ); diff --git a/dlls/user32/controls.h b/dlls/user32/controls.h index 6178100cc27..594b665073f 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/tests/class.c b/dlls/user32/tests/class.c index a988083cf58..577a7b858a6 100644 --- a/dlls/user32/tests/class.c +++ b/dlls/user32/tests/class.c @@ -39,6 +39,8 @@ static const WCHAR WC_EDITW[] = {'E','d','i','t',0}; #define NUMCLASSWORDS 4 +#define IS_WNDPROC_HANDLE(x) (((ULONG_PTR)(x) >> 16) == (~((ULONG_PTR)0) >> 16)) + static LRESULT WINAPI ClassTest_WndProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { return DefWindowProcW (hWnd, msg, wParam, lParam); @@ -565,12 +567,23 @@ 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[][10] = { + "Button", + "Static", + "ComboBox", + "ComboLBox", + "ListBox", + "#32770", /* dialog */ + }; + 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 */ WNDPROC pDefWindowProcA, pDefWindowProcW; + WNDPROC oldproc; WNDCLASSEXA cls; /* the memory layout of WNDCLASSEXA and WNDCLASSEXW is the same */ WCHAR buf[128]; ATOM atom; @@ -662,10 +675,43 @@ 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]); + + oldproc = (WNDPROC)SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (LONG_PTR)ClassTest_WndProc); + ok(IS_WNDPROC_HANDLE(oldproc) == FALSE, "Class %s shouldn't return a handle\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 treated specially. */ hwnd = CreateWindowW(WC_EDITW, unistring, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 680, 260, NULL, NULL, NULL, 0); + /* GetClassLongPtr returns that both the Unicode and ANSI wndproc */ + ok(IS_WNDPROC_HANDLE(GetClassLongPtrA(hwnd, GCLP_WNDPROC)) == FALSE, "Edit control class should have a Unicode wndproc\n"); + ok(IS_WNDPROC_HANDLE(GetClassLongPtrW(hwnd, GCLP_WNDPROC)) == FALSE, "Edit control class should have a ANSI wndproc\n"); + /* But GetWindowLongPtr returns only a handle for the ANSI one */ + ok(IS_WNDPROC_HANDLE(GetWindowLongPtrA(hwnd, GWLP_WNDPROC)), "Edit control should return a wndproc handle\n"); + ok(!IS_WNDPROC_HANDLE(GetWindowLongPtrW(hwnd, GWLP_WNDPROC)), "Edit control shouldn't return a W 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); @@ -677,7 +723,9 @@ static void test_defwndproc(void) CallWindowProcA((WNDPROC)GetWindowLongPtrA(hwnd, GWLP_WNDPROC), hwnd, WM_GETTEXT, 120, (LPARAM)buf); ok(memcmp(buf, classA, sizeof(classA)) == 0, "WM_GETTEXT invalid return\n"); - SetWindowLongPtrA(hwnd, GWLP_WNDPROC, (LONG_PTR)ClassTest_WndProc2); + oldproc = (WNDPROC)SetWindowLongPtrA(hwnd, GWLP_WNDPROC, (LONG_PTR)ClassTest_WndProc2); + /* SetWindowLongPtr returns a wndproc handle - like GetWindowLongPtr */ + ok(IS_WNDPROC_HANDLE(oldproc), "Edit control should return a wndproc handle\n"); ok(IsWindowUnicode(hwnd) == FALSE, "SetWindowLongPtrA should have changed window to ANSI\n"); SetWindowTextA(hwnd, classA); /* Windows resets the title to WideStringToMultiByte(unistring) */ memset(buf, 0, sizeof(buf)); @@ -696,6 +744,12 @@ static void test_defwndproc(void) hwnd = CreateWindowA(WC_EDITA, classA, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 680, 260, NULL, NULL, NULL, 0); + /* GetClassLongPtr returns that both the Unicode and ANSI wndproc */ + ok(!IS_WNDPROC_HANDLE(GetClassLongPtrA(hwnd, GCLP_WNDPROC)), "Edit control class should have a Unicode wndproc\n"); + ok(!IS_WNDPROC_HANDLE(GetClassLongPtrW(hwnd, GCLP_WNDPROC)), "Edit control class should have a ANSI wndproc\n"); + /* But GetWindowLongPtr returns only a handle for the Unicode one */ + ok(!IS_WNDPROC_HANDLE(GetWindowLongPtrA(hwnd, GWLP_WNDPROC)), "Edit control shouldn't return an A wndproc handle\n"); + 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 +839,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 41eb108470f..6aea6c90936 100644 --- a/dlls/user32/win.c +++ b/dlls/user32/win.c @@ -1788,8 +1788,17 @@ static LONG_PTR WIN_GetWindowLong( HWND hwnd, INT offset, UINT size, BOOL unicod 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_HINSTANCE: retvalue = (ULONG_PTR)wndPtr->hInstance; 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 && (!unicode != !(wndPtr->flags & WIN_ISUNICODE))) + retvalue = (ULONG_PTR)wndPtr->winproc; + else + retvalue = (ULONG_PTR)WINPROC_GetProc( wndPtr->winproc, unicode ); + break; default: WARN("Unknown offset %d\n", offset ); SetLastError( ERROR_INVALID_INDEX ); @@ -1877,7 +1886,7 @@ LONG_PTR WIN_SetWindowLong( HWND hwnd, INT offset, UINT size, LONG_PTR newval, B { WNDPROC proc; UINT old_flags = wndPtr->flags; - retval = (ULONG_PTR)WINPROC_GetProc( wndPtr->winproc, unicode ); + retval = WIN_GetWindowLong( hwnd, offset, size, unicode ); if (unicode) proc = WINPROC_AllocProc( NULL, (WNDPROC)newval ); else proc = WINPROC_AllocProc( (WNDPROC)newval, NULL ); if (proc) wndPtr->winproc = proc; diff --git a/dlls/user32/winproc.c b/dlls/user32/winproc.c index 51936afb71d..7d6fa36c316 100644 --- a/dlls/user32/winproc.c +++ b/dlls/user32/winproc.c @@ -54,6 +54,8 @@ typedef struct tagWINDOWPROC #define MAX_WINPROCS 8192 #define BUILTIN_WINPROCS 8 /* first BUILTIN_WINPROCS entries are reserved for builtin procs */ +WNDPROC EDIT_winproc_handle = 0; + static WINDOWPROC winproc_array[MAX_WINPROCS]; static UINT builtin_used; static UINT winproc_used = BUILTIN_WINPROCS; @@ -113,19 +115,6 @@ static inline WINDOWPROC *find_winproc( WNDPROC funcA, WNDPROC funcW ) 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 +2277,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 +2303,7 @@ LRESULT WINAPI CallWindowProcW( WNDPROC func, HWND hwnd, UINT msg, 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 +2361,7 @@ INT_PTR WINPROC_CallDlgProcA( DLGPROC func, HWND hwnd, UINT msg, WPARAM wParam, 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 +2391,7 @@ INT_PTR WINPROC_CallDlgProcW( DLGPROC func, HWND hwnd, UINT msg, WPARAM wParam, 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)