user32: Better fix how to handle GetWindowLongPtr[AW](..., GWLP_WNDPROC) for builtin winprocs.

This commit is contained in:
Mikolaj Zalewski 2007-10-17 18:55:55 -07:00 committed by Alexandre Julliard
parent 55d274d1d2
commit 585329ed13
5 changed files with 83 additions and 47 deletions

View File

@ -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 );

View File

@ -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;

View File

@ -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();
}

View File

@ -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;

View File

@ -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)