-- v2: user32: Pass real argument to NtUserGetClassName in RealGetWindowClass. win32u: Add support for retrieving real window class ID across processes. user32: Set real window class ID for user32 standard controls.
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/user32/tests/class.c | 293 +++++++++++++++++++++++++++++++++++++- 1 file changed, 292 insertions(+), 1 deletion(-)
diff --git a/dlls/user32/tests/class.c b/dlls/user32/tests/class.c index 686fced55ee..57e2dd19e3f 100644 --- a/dlls/user32/tests/class.c +++ b/dlls/user32/tests/class.c @@ -1592,6 +1592,291 @@ static void test_class_name(void) UnregisterClassW(class_name, hinst); }
+#define SUPER_CLASS_NAME_W L"SuperClass Test" +#define SUPER_CLASS_NAME_A "SuperClass Test" +struct real_class_test { + const WCHAR *class_name_w; + const char *class_name_a; + const WCHAR *real_class_name_w; + const char *real_class_name_a; + BOOL test_cross_process; + BOOL wine_todo; +}; + +static const struct real_class_test class_tests[] = { + { L"Button", "Button", L"Button", "Button", TRUE, TRUE }, + { L"ComboBox", "ComboBox", L"ComboBox", "ComboBox", TRUE, TRUE }, + { L"Edit", "Edit", L"Edit", "Edit", TRUE, TRUE }, + { L"ListBox", "ListBox", L"ListBox", "ListBox", TRUE, TRUE }, + { L"ScrollBar", "ScrollBar", L"ScrollBar", "ScrollBar", TRUE, TRUE }, + { L"Static", "Static", L"Static", "Static", TRUE, TRUE }, + { L"ComboLBox", "ComboLBox", L"ListBox", "ListBox", TRUE, TRUE }, + { L"MDIClient", "MDIClient", L"MDIClient", "MDIClient", TRUE, TRUE }, + { L"#32768", "#32768", L"#32768", "#32768", TRUE, TRUE }, + { L"#32770", "#32770", L"#32770", "#32770", TRUE, TRUE }, + /* Not all built-in classes set real window class. */ + { L"Message", "Message", SUPER_CLASS_NAME_W, SUPER_CLASS_NAME_A, FALSE, FALSE }, +}; + +static const struct real_class_test comctl32_class_tests[] = { + { L"Button", "Button", SUPER_CLASS_NAME_W, SUPER_CLASS_NAME_A, }, + { L"ComboBox", "ComboBox", SUPER_CLASS_NAME_W, SUPER_CLASS_NAME_A, }, + { L"Edit", "Edit", SUPER_CLASS_NAME_W, SUPER_CLASS_NAME_A, }, + { L"ListBox", "ListBox", SUPER_CLASS_NAME_W, SUPER_CLASS_NAME_A, }, + { L"ScrollBar", "ScrollBar", SUPER_CLASS_NAME_W, SUPER_CLASS_NAME_A, }, + { L"Static", "Static", SUPER_CLASS_NAME_W, SUPER_CLASS_NAME_A, }, + { L"ComboLBox", "ComboLBox", SUPER_CLASS_NAME_W, SUPER_CLASS_NAME_A, }, +}; + +static WNDPROC real_class_wndproc; +static BOOL pass_msg_to_real_class_wndproc; +static LRESULT WINAPI super_class_test_win_proc_w(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + if (pass_msg_to_real_class_wndproc && real_class_wndproc) + CallWindowProcW(real_class_wndproc, hwnd, msg, wparam, lparam); + + if (msg == WM_NCCREATE) + return 1; + + return DefWindowProcW(hwnd, msg, wparam, lparam); +} + +static LRESULT WINAPI super_class_test_win_proc_a(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + if (pass_msg_to_real_class_wndproc && real_class_wndproc) + CallWindowProcA(real_class_wndproc, hwnd, msg, wparam, lparam); + + if (msg == WM_NCCREATE) + return 1; + + return DefWindowProcA(hwnd, msg, wparam, lparam); +} + +#define test_hwnd_real_class_name_a_w( hwnd, exp_real_class_name_w, exp_real_class_name_a, todo ) \ + test_hwnd_real_class_name_a_w_( (hwnd), (exp_real_class_name_w), (exp_real_class_name_a), (todo), __FILE__, __LINE__ ) +static void test_hwnd_real_class_name_a_w_(HWND hwnd, const WCHAR *exp_real_class_name_w, + const char *exp_real_class_name_a, BOOL todo, const char *file, int line) +{ + WCHAR real_class_name_w[256] = { 0 }; + char real_class_name_a[256] = { 0 }; + ULONG len; + + len = RealGetWindowClassW(hwnd, real_class_name_w, ARRAY_SIZE(real_class_name_w)); + todo_wine_if(todo) ok(!lstrcmpW(real_class_name_w, exp_real_class_name_w), "got %s\n", debugstr_w(real_class_name_w)); + todo_wine_if(todo) ok(len == lstrlenW(exp_real_class_name_w), "got %ld, %s\n", len, debugstr_w(exp_real_class_name_w)); + + len = RealGetWindowClassA(hwnd, real_class_name_a, ARRAY_SIZE(real_class_name_a)); + todo_wine_if(todo) ok(!strcmp(real_class_name_a, exp_real_class_name_a), "got %s\n", real_class_name_a); + todo_wine_if(todo) ok(len == strlen(exp_real_class_name_a), "got %ld, %s\n", len, exp_real_class_name_a); +} + +static void test_real_class_name(const struct real_class_test *class_test, BOOL wide_string, int idx) +{ + const CLIENTCREATESTRUCT client_cs = { NULL, 1 }; /* Needed for MDIClient. */ + HWND hwnd; + int i; + + /* + * First pass doesn't pass messages through to the class window procedure, + * second pass does. + */ + for (i = 0; i < 2; i++) + { + pass_msg_to_real_class_wndproc = i; + + if (wide_string) + hwnd = CreateWindowW(SUPER_CLASS_NAME_W, L"test", WS_OVERLAPPED, 0, 0, 50, 50, 0, 0, 0, (void *)&client_cs); + else + hwnd = CreateWindowA(SUPER_CLASS_NAME_A, "test", WS_OVERLAPPED, 0, 0, 50, 50, 0, 0, 0, (void *)&client_cs); + ok(!!hwnd, "hwnd == NULL\n"); + + if (!i) + test_hwnd_real_class_name_a_w(hwnd, SUPER_CLASS_NAME_W, SUPER_CLASS_NAME_A, FALSE); + else + { + test_hwnd_real_class_name_a_w(hwnd, class_test->real_class_name_w, class_test->real_class_name_a, + class_test->wine_todo); + if (class_test->test_cross_process) + { + char path_name[MAX_PATH]; + PROCESS_INFORMATION info; + STARTUPINFOA startup; + char **argv; + + winetest_get_mainargs(&argv); + memset(&startup, 0, sizeof(startup)); + startup.cb = sizeof(startup); + sprintf( path_name, "%s %s test_cross_process_RealGetWindowClass %d %#lx", argv[0], argv[1], idx, HandleToUlong(hwnd)); + ok(CreateProcessA(NULL, path_name, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &info), "CreateProcess failed.\n"); + wait_child_process(info.hProcess); + CloseHandle(info.hProcess); + CloseHandle(info.hThread); + } + } + + DestroyWindow(hwnd); + } +} + +static void test_real_class_names(const struct real_class_test *tests, int tests_count) +{ + WNDCLASSW cls_w; + WNDCLASSA cls_a; + BOOL ret; + int i; + + for (i = 0; i < tests_count; i++) + { + const struct real_class_test *class_test = &tests[i]; + + /* Test W version of functions. */ + memset(&cls_w, 0, sizeof(cls_w)); + ret = GetClassInfoW(NULL, class_test->class_name_w, &cls_w); + ok(ret, "GetClassInfoW failed: %lu\n", GetLastError()); + + real_class_wndproc = cls_w.lpfnWndProc; + cls_w.lpfnWndProc = super_class_test_win_proc_w; + cls_w.hInstance = GetModuleHandleW(NULL); + cls_w.lpszClassName = SUPER_CLASS_NAME_W; + RegisterClassW(&cls_w); + + test_real_class_name(class_test, TRUE, i); + + UnregisterClassW(SUPER_CLASS_NAME_W, GetModuleHandleW(NULL)); + + /* Test A version of functions. */ + memset(&cls_a, 0, sizeof(cls_a)); + ret = GetClassInfoA(NULL, class_test->class_name_a, &cls_a); + ok(ret, "GetClassInfoA failed: %lu\n", GetLastError()); + + real_class_wndproc = cls_a.lpfnWndProc; + cls_a.lpfnWndProc = super_class_test_win_proc_a; + cls_a.hInstance = GetModuleHandleA(NULL); + cls_a.lpszClassName = SUPER_CLASS_NAME_A; + RegisterClassA(&cls_a); + + test_real_class_name(class_test, FALSE, i); + + UnregisterClassA(SUPER_CLASS_NAME_A, GetModuleHandleA(NULL)); + } +} + +static void test_RealGetWindowClass(void) +{ + char path_name[MAX_PATH]; + PROCESS_INFORMATION info; + WCHAR class_name_w[20]; + STARTUPINFOA startup; + char class_name[20]; + char **argv; + HWND hwnd; + UINT ret; + + hwnd = CreateWindowA("Button", "test", BS_CHECKBOX | WS_POPUP, 0, 0, 50, 14, 0, 0, 0, NULL); + ok(!!hwnd, "hwnd == NULL\n"); + + /* Normal tests, just get the name of the class, no shenanigans. */ + memset(class_name, 0, sizeof(class_name)); + ret = RealGetWindowClassA(hwnd, class_name, ARRAY_SIZE(class_name)); + ok(!strcmp(class_name, "Button"), "got %s\n", class_name); + ok(ret == strlen(class_name), "got %d, %s\n", ret, class_name); + + memset(class_name_w, 0, sizeof(class_name_w)); + ret = RealGetWindowClassW(hwnd, class_name_w, ARRAY_SIZE(class_name_w)); + ok(!lstrcmpW(class_name_w, L"Button"), "got %s\n", debugstr_w(class_name_w)); + ok(ret == lstrlenW(class_name_w), "got %d, %s\n", ret, debugstr_w(class_name_w)); + + /* Shortened buffer tests. */ + memset(class_name, 0, sizeof(class_name)); + ret = RealGetWindowClassA(hwnd, class_name, 2); + ok(!strcmp(class_name, "B"), "got %s\n", class_name); + ok(ret == strlen(class_name), "got %d\n", ret); + + memset(class_name_w, 0, sizeof(class_name_w)); + ret = RealGetWindowClassW(hwnd, class_name_w, 2); + ok(!lstrcmpW(class_name_w, L"B"), "got %s\n", debugstr_w(class_name_w)); + ok(ret == lstrlenW(class_name_w), "got %d, %s\n", ret, debugstr_w(class_name_w)); + + /* A NULL buffer with a non-zero length will result in an access violation. */ + if (0) + { + RealGetWindowClassA(hwnd, NULL, ARRAY_SIZE(class_name)); + } + + /* Invalid length. */ + memset(class_name, 0, sizeof(class_name)); + SetLastError(0xdeadbeef); + ret = RealGetWindowClassA(hwnd, class_name, 0); + ok(!ret, "got %d\n", ret); + todo_wine ok((GetLastError() == ERROR_INSUFFICIENT_BUFFER), "Unexpected last error %ld\n", GetLastError()); + + memset(class_name_w, 0, sizeof(class_name_w)); + SetLastError(0xdeadbeef); + ret = RealGetWindowClassW(hwnd, class_name_w, 0); + ok(!ret, "got %d\n", ret); + ok((GetLastError() == ERROR_INSUFFICIENT_BUFFER), "Unexpected last error %ld\n", GetLastError()); + + DestroyWindow(hwnd); + + test_real_class_names(class_tests, ARRAY_SIZE(class_tests)); + + /* Test RealGetWindowClass on comctl32 version of these controls. */ + winetest_get_mainargs(&argv); + memset(&startup, 0, sizeof(startup)); + startup.cb = sizeof(startup); + sprintf( path_name, "%s %s test_comctl32_RealGetWindowClass", argv[0], argv[1]); + ok(CreateProcessA(NULL, path_name, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &info), "CreateProcess failed.\n"); + wait_child_process(info.hProcess); + CloseHandle(info.hProcess); + CloseHandle(info.hThread); +} + +static void test_comctl32_RealGetWindowClass(void) +{ + char path[MAX_PATH]; + ULONG_PTR cookie; + HMODULE module; + HANDLE context; + BOOL ret; + + /* + * If the comctl32 version of standard controls are used, "real" window + * class is not set. Applications are expected to use + * OBJID_QUERYCLASSNAMEIDX instead. + */ + GetTempPathA(ARRAY_SIZE(path), path); + strcat(path, "comctl32_class.manifest"); + + create_manifest_file(path, comctl32_manifest); + context = create_test_actctx(path); + ret = DeleteFileA(path); + ok(ret, "Failed to delete manifest file, error %ld.\n", GetLastError()); + + module = GetModuleHandleA("comctl32"); + ok(!module, "comctl32 already loaded\n"); + + ret = ActivateActCtx(context, &cookie); + ok(ret, "Failed to activate context.\n"); + module = GetModuleHandleA("comctl32"); + ok(!module, "comctl32 already loaded\n"); + + test_real_class_names(comctl32_class_tests, ARRAY_SIZE(comctl32_class_tests)); + + module = GetModuleHandleA("comctl32"); + ok(!!module, "comctl32 not loaded\n"); + + ret = DeactivateActCtx(0, cookie); + ok(ret, "Failed to deactivate context.\n"); + ReleaseActCtx(context); +} + +static void test_cross_process_RealGetWindowClass(int real_class_test_idx, HWND hwnd) +{ + const struct real_class_test *class_test = &class_tests[real_class_test_idx]; + + test_hwnd_real_class_name_a_w(hwnd, class_test->real_class_name_w, class_test->real_class_name_a, class_test->wine_todo); +} + START_TEST(class) { char **argv; @@ -1600,7 +1885,12 @@ START_TEST(class)
if (argc >= 3) { - test_comctl32_class( argv[2] ); + if (!strcmp(argv[2], "test_comctl32_RealGetWindowClass")) + test_comctl32_RealGetWindowClass(); + else if (!strcmp(argv[2], "test_cross_process_RealGetWindowClass")) + test_cross_process_RealGetWindowClass(strtol(argv[3], NULL, 0), UlongToHandle(strtol(argv[4], NULL, 16)) ); + else + test_comctl32_class( argv[2] ); return; }
@@ -1626,6 +1916,7 @@ START_TEST(class) test_comctl32_classes(); test_actctx_classes(); test_class_name(); + test_RealGetWindowClass();
/* this test unregisters the Button class so it should be executed at the end */ test_instances();
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/win32u/tests/win32u.c | 54 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+)
diff --git a/dlls/win32u/tests/win32u.c b/dlls/win32u/tests/win32u.c index e118d081ead..1d70eb9fbb0 100644 --- a/dlls/win32u/tests/win32u.c +++ b/dlls/win32u/tests/win32u.c @@ -126,6 +126,18 @@ static void test_window_props(void) DestroyWindow( hwnd ); }
+static WNDPROC real_class_wndproc; +static LRESULT WINAPI real_class_test_win_proc_w(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + if (real_class_wndproc) + CallWindowProcW(real_class_wndproc, hwnd, msg, wparam, lparam); + + if (msg == WM_NCCREATE) + return 1; + + return DefWindowProcW(hwnd, msg, wparam, lparam); +} + static void test_class(void) { UNICODE_STRING name; @@ -219,6 +231,48 @@ static void test_class(void) "NtUserGetAtomName returned %lx %lu\n", ret, GetLastError() ); ok( buf[0] == 0xcccc, "buf = %s\n", debugstr_w(buf) );
+ memset( &cls, 0, sizeof(cls) ); + ret = GetClassInfoW( NULL, L"Edit", &cls ); + ok( ret, "GetClassInfoW failed: %lu\n", GetLastError() ); + + real_class_wndproc = cls.lpfnWndProc; + cls.lpfnWndProc = real_class_test_win_proc_w; + cls.hInstance = GetModuleHandleW( NULL ); + cls.lpszClassName = L"test"; + + class = RegisterClassW( &cls ); + ok( class, "RegisterClassW failed: %lu\n", GetLastError() ); + + hwnd = CreateWindowW( L"test", L"test name", WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL, + CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, 0, 0, NULL, 0 ); + + /* Get real class, in this case Edit. */ + memset( buf, 0xcc, sizeof(buf) ); + name.Buffer = buf; + name.Length = 0xdead; + name.MaximumLength = sizeof(buf); + ret = NtUserGetClassName( hwnd, TRUE, &name ); + ok( ret == 4, "NtUserGetClassName returned %lu\n", ret ); + ok( name.Length == 0xdead, "Length = %u\n", name.Length ); + ok( name.MaximumLength == sizeof(buf), "MaximumLength = %u\n", name.MaximumLength ); + todo_wine ok( !wcscmp( buf, L"Edit" ), "buf = %s\n", debugstr_w(buf) ); + + /* Get normal class instead of real class. */ + memset( buf, 0xcc, sizeof(buf) ); + name.Buffer = buf; + name.Length = 0xdead; + name.MaximumLength = sizeof(buf); + ret = NtUserGetClassName( hwnd, FALSE, &name ); + ok( ret == 4, "NtUserGetClassName returned %lu\n", ret ); + ok( name.Length == 0xdead, "Length = %u\n", name.Length ); + ok( name.MaximumLength == sizeof(buf), "MaximumLength = %u\n", name.MaximumLength ); + ok( !wcscmp( buf, L"test" ), "buf = %s\n", debugstr_w(buf) ); + + DestroyWindow( hwnd ); + + ret = UnregisterClassW( L"test", GetModuleHandleW(NULL) ); + ok( ret, "UnregisterClassW failed: %lu\n", GetLastError() ); + real_class_wndproc = NULL; }
static void test_NtUserCreateInputContext(void)
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/user32/button.c | 1 + dlls/user32/combo.c | 1 + dlls/user32/defdlg.c | 2 ++ dlls/user32/edit.c | 1 + dlls/user32/listbox.c | 1 + dlls/user32/mdi.c | 1 + dlls/user32/menu.c | 4 +++- dlls/user32/scroll.c | 4 +++- dlls/user32/static.c | 1 + dlls/win32u/class.c | 38 +++++++++++++++++++++++++++++++++--- dlls/win32u/ntuser_private.h | 1 + dlls/win32u/tests/win32u.c | 2 +- dlls/win32u/window.c | 22 +++++++++++++++++++++ include/ntuser.h | 21 ++++++++++++++++++++ 14 files changed, 94 insertions(+), 6 deletions(-)
diff --git a/dlls/user32/button.c b/dlls/user32/button.c index 10a111562a7..24d5ac843e2 100644 --- a/dlls/user32/button.c +++ b/dlls/user32/button.c @@ -202,6 +202,7 @@ LRESULT ButtonWndProc_common(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, WIN_SetStyle( hWnd, style, BS_TYPEMASK & ~style ); } set_button_state( hWnd, BST_UNCHECKED ); + NtUserSetRealClassId( hWnd, REAL_CLASS_ID_BUTTON ); return 0;
case WM_ERASEBKGND: diff --git a/dlls/user32/combo.c b/dlls/user32/combo.c index 6afd841f82a..73fa2422e52 100644 --- a/dlls/user32/combo.c +++ b/dlls/user32/combo.c @@ -537,6 +537,7 @@ static LRESULT COMBO_Create( HWND hwnd, LPHEADCOMBO lphc, HWND hwndParent, LONG CBForceDummyResize(lphc); }
+ NtUserSetRealClassId(hwnd, REAL_CLASS_ID_COMBOBOX); TRACE("init done\n"); return 0; } diff --git a/dlls/user32/defdlg.c b/dlls/user32/defdlg.c index 88b64ee469c..c6462c0b879 100644 --- a/dlls/user32/defdlg.c +++ b/dlls/user32/defdlg.c @@ -416,6 +416,8 @@ static LRESULT USER_DefDlgProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPar
LRESULT WINAPI USER_DefDlgProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, BOOL unicode ) { + if (msg == WM_CREATE) + NtUserSetRealClassId( hwnd, REAL_CLASS_ID_DIALOG ); if (unicode) return USER_DefDlgProcW( hwnd, msg, wParam, lParam ); else diff --git a/dlls/user32/edit.c b/dlls/user32/edit.c index 39f429813d6..59459d41658 100644 --- a/dlls/user32/edit.c +++ b/dlls/user32/edit.c @@ -4928,6 +4928,7 @@ LRESULT EditWndProc_common( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, B result = EDIT_WM_Create(es, nameW); HeapFree(GetProcessHeap(), 0, nameW); } + NtUserSetRealClassId(hwnd, REAL_CLASS_ID_EDIT); break;
case WM_CUT: diff --git a/dlls/user32/listbox.c b/dlls/user32/listbox.c index 0e925973e84..3dc1e54e91d 100644 --- a/dlls/user32/listbox.c +++ b/dlls/user32/listbox.c @@ -2652,6 +2652,7 @@ LRESULT ListBoxWndProc_common( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam CREATESTRUCTW *lpcs = (CREATESTRUCTW *)lParam; if (lpcs->style & LBS_COMBOBOX) lphc = lpcs->lpCreateParams; if (!LISTBOX_Create( hwnd, lphc )) return -1; + NtUserSetRealClassId( hwnd, REAL_CLASS_ID_LISTBOX ); TRACE("creating hwnd %p descr %p\n", hwnd, (void *)GetWindowLongPtrW( hwnd, 0 ) ); return 0; } diff --git a/dlls/user32/mdi.c b/dlls/user32/mdi.c index f579298544a..a45ab3383f2 100644 --- a/dlls/user32/mdi.c +++ b/dlls/user32/mdi.c @@ -1025,6 +1025,7 @@ LRESULT MDIClientWndProc_common( HWND hwnd, UINT message, WPARAM wParam, LPARAM
if (!hBmpClose) hBmpClose = CreateMDIMenuBitmap();
+ NtUserSetRealClassId(hwnd, REAL_CLASS_ID_MDI_CLIENT); TRACE("Client created: hwnd %p, Window menu %p, idFirst = %04x\n", hwnd, ci->hWindowMenu, ci->idFirstChild ); return 0; diff --git a/dlls/user32/menu.c b/dlls/user32/menu.c index 4ba6fa71028..47457117b3a 100644 --- a/dlls/user32/menu.c +++ b/dlls/user32/menu.c @@ -192,8 +192,10 @@ LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM { switch(message) { - case WM_DESTROY: case WM_CREATE: + NtUserSetRealClassId( hwnd, REAL_CLASS_ID_MENU ); + /* Fall through. */ + case WM_DESTROY: case WM_MOUSEACTIVATE: case WM_PAINT: case WM_PRINTCLIENT: diff --git a/dlls/user32/scroll.c b/dlls/user32/scroll.c index 788b5091698..b1f460c032f 100644 --- a/dlls/user32/scroll.c +++ b/dlls/user32/scroll.c @@ -237,6 +237,9 @@ LRESULT WINAPI USER_ScrollBarProc( HWND hwnd, UINT message, WPARAM wParam, LPARA { switch(message) { + case WM_CREATE: + NtUserSetRealClassId( hwnd, REAL_CLASS_ID_SCROLLBAR ); + /* Fall through. */ case WM_KEYDOWN: case WM_KEYUP: case WM_ENABLE: @@ -250,7 +253,6 @@ LRESULT WINAPI USER_ScrollBarProc( HWND hwnd, UINT message, WPARAM wParam, LPARA case WM_SYSTIMER: case WM_SETFOCUS: case WM_KILLFOCUS: - case WM_CREATE: case WM_ERASEBKGND: case WM_GETDLGCODE: case WM_PAINT: diff --git a/dlls/user32/static.c b/dlls/user32/static.c index f3953f7610a..40c2d9fbfb9 100644 --- a/dlls/user32/static.c +++ b/dlls/user32/static.c @@ -333,6 +333,7 @@ LRESULT StaticWndProc_common( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ERR("Unknown style 0x%02lx\n", style ); return -1; } + NtUserSetRealClassId(hwnd, REAL_CLASS_ID_STATIC); STATIC_InitColours(); break;
diff --git a/dlls/win32u/class.c b/dlls/win32u/class.c index 396e2285797..b35618d80c2 100644 --- a/dlls/win32u/class.c +++ b/dlls/win32u/class.c @@ -612,13 +612,26 @@ ULONG WINAPI NtUserGetAtomName( ATOM atom, UNICODE_STRING *name ) return size / sizeof(WCHAR); }
+static const WCHAR real_class_id_str[][MAX_ATOM_LEN + 1] = { + { 'B', 'u', 't', 't', 'o', 'n', 0, }, /* REAL_CLASS_ID_BUTTON */ + { 'C', 'o', 'm', 'b', 'o', 'B', 'o', 'x', 0, }, /* REAL_CLASS_ID_COMBOBOX */ + { 'E', 'd', 'i', 't', 0, }, /* REAL_CLASS_ID_EDIT */ + { 'L', 'i', 's', 't', 'B', 'o', 'x', 0, }, /* REAL_CLASS_ID_LISTBOX */ + { 'S', 'c', 'r', 'o', 'l', 'l', 'B', 'a', 'r', 0, }, /* REAL_CLASS_ID_SCROLLBAR */ + { 'S', 't', 'a', 't', 'i', 'c', 0, }, /* REAL_CLASS_ID_STATIC */ + { 'M', 'D', 'I', 'C', 'l', 'i', 'e', 'n', 't', 0, }, /* REAL_CLASS_ID_MDI_CLIENT */ + { '#', '3', '2', '7', '6', '8', 0, }, /* REAL_CLASS_ID_MENU */ + { '#', '3', '2', '7', '7', '0', 0, }, /* REAL_CLASS_ID_DIALOG */ +}; + /*********************************************************************** * NtUserGetClassName (win32u.@) */ INT WINAPI NtUserGetClassName( HWND hwnd, BOOL real, UNICODE_STRING *name ) { + const WCHAR *basename = NULL; CLASS *class; - int ret; + int ret = 0;
TRACE( "%p %x %p\n", hwnd, real, name );
@@ -648,9 +661,28 @@ INT WINAPI NtUserGetClassName( HWND hwnd, BOOL real, UNICODE_STRING *name ) return NtUserGetAtomName( atom, name ); }
- ret = min( name->MaximumLength / sizeof(WCHAR) - 1, lstrlenW(class->basename) ); - if (ret) memcpy( name->Buffer, class->basename, ret * sizeof(WCHAR) ); + if (real) + { + WND *wnd; + + if (!(wnd = get_win_ptr( hwnd ))) + { + RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE ); + goto exit; + } + + if (wnd->real_class_id) + basename = real_class_id_str[wnd->real_class_id - 1]; + release_win_ptr(wnd); + } + + if (!basename) + basename = (const WCHAR *)class->basename; + + ret = min( name->MaximumLength / sizeof(WCHAR) - 1, lstrlenW(basename) ); + if (ret) memcpy( name->Buffer, basename, ret * sizeof(WCHAR) ); name->Buffer[ret] = 0; +exit: release_class_ptr( class ); return ret; } diff --git a/dlls/win32u/ntuser_private.h b/dlls/win32u/ntuser_private.h index b39e38db5d6..3b861f0200a 100644 --- a/dlls/win32u/ntuser_private.h +++ b/dlls/win32u/ntuser_private.h @@ -86,6 +86,7 @@ typedef struct tagWND struct tagDIALOGINFO *dlgInfo; /* Dialog additional info (dialogs only) */ int pixel_format; /* Pixel format set by the graphics driver */ int internal_pixel_format; /* Internal pixel format set via WGL_WINE_pixel_format_passthrough */ + unsigned int real_class_id; /* "Real" window class ID, set by user32 standard control window procedures. */ int cbWndExtra; /* class cbWndExtra at window creation */ DWORD_PTR userdata; /* User private data */ DWORD wExtra[1]; /* Window extra bytes */ diff --git a/dlls/win32u/tests/win32u.c b/dlls/win32u/tests/win32u.c index 1d70eb9fbb0..e2a84f448ab 100644 --- a/dlls/win32u/tests/win32u.c +++ b/dlls/win32u/tests/win32u.c @@ -255,7 +255,7 @@ static void test_class(void) ok( ret == 4, "NtUserGetClassName returned %lu\n", ret ); ok( name.Length == 0xdead, "Length = %u\n", name.Length ); ok( name.MaximumLength == sizeof(buf), "MaximumLength = %u\n", name.MaximumLength ); - todo_wine ok( !wcscmp( buf, L"Edit" ), "buf = %s\n", debugstr_w(buf) ); + ok( !wcscmp( buf, L"Edit" ), "buf = %s\n", debugstr_w(buf) );
/* Get normal class instead of real class. */ memset( buf, 0xcc, sizeof(buf) ); diff --git a/dlls/win32u/window.c b/dlls/win32u/window.c index 4a57e0abde7..ae9a24125e5 100644 --- a/dlls/win32u/window.c +++ b/dlls/win32u/window.c @@ -4491,6 +4491,25 @@ BOOL show_owned_popups( HWND owner, BOOL show ) return TRUE; }
+static BOOL set_real_window_class_id( HWND hwnd, DWORD param ) +{ + WND *win = get_win_ptr( hwnd ); + + if (!win || win == WND_OTHER_PROCESS || win == WND_DESKTOP) + { + ERR("Can't set real window class for a window belonging to another process\n"); + return FALSE; + } + + if (!win->real_class_id) + { + FIXME("Real class ID currently set in-process only.\n"); + win->real_class_id = param; + } + release_win_ptr( win ); + return TRUE; +} + /******************************************************************* * NtUserFlashWindowEx (win32u.@) */ @@ -5595,6 +5614,9 @@ ULONG_PTR WINAPI NtUserCallHwndParam( HWND hwnd, DWORD_PTR param, DWORD code ) return set_window_style( hwnd, style->styleNew, style->styleOld ); }
+ case NtUserCallHwndParam_SetRealClassId: + return set_real_window_class_id( hwnd, param ); + default: FIXME( "invalid code %u\n", (int)code ); return 0; diff --git a/include/ntuser.h b/include/ntuser.h index 171d32abe6e..f9211277eee 100644 --- a/include/ntuser.h +++ b/include/ntuser.h @@ -513,6 +513,21 @@ struct ime_driver_call_params
#define WM_SYSTIMER 0x0118
+/* Internal real window class ID values. */ +enum wine_real_class_id +{ + REAL_CLASS_ID_NONE = 0, + REAL_CLASS_ID_BUTTON, + REAL_CLASS_ID_COMBOBOX, + REAL_CLASS_ID_EDIT, + REAL_CLASS_ID_LISTBOX, + REAL_CLASS_ID_SCROLLBAR, + REAL_CLASS_ID_STATIC, + REAL_CLASS_ID_MDI_CLIENT, + REAL_CLASS_ID_MENU, + REAL_CLASS_ID_DIALOG, +}; +
HKL WINAPI NtUserActivateKeyboardLayout( HKL layout, UINT flags ); BOOL WINAPI NtUserAddClipboardFormatListener( HWND hwnd ); @@ -1208,6 +1223,7 @@ enum NtUserCallHwndParam_SetMDIClientInfo, NtUserCallHwndParam_SetWindowContextHelpId, NtUserCallHwndParam_ShowOwnedPopups, + NtUserCallHwndParam_SetRealClassId, /* temporary exports */ NtUserSetWindowStyle, }; @@ -1378,6 +1394,11 @@ static inline BOOL NtUserShowOwnedPopups( HWND hwnd, BOOL show ) return NtUserCallHwndParam( hwnd, show, NtUserCallHwndParam_ShowOwnedPopups ); }
+static inline BOOL NtUserSetRealClassId( HWND hwnd, DWORD real_class_id ) +{ + return NtUserCallHwndParam( hwnd, real_class_id, NtUserCallHwndParam_SetRealClassId ); +} + /* Wine extensions */ BOOL WINAPI __wine_send_input( HWND hwnd, const INPUT *input, const RAWINPUT *rawinput );
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/win32u/class.c | 52 +++++++++++++++++----------------- dlls/win32u/window.c | 8 +++++- include/wine/server_protocol.h | 36 ++++++++++++++++++++++- server/protocol.def | 16 +++++++++++ server/request.h | 12 ++++++++ server/trace.c | 23 +++++++++++++++ server/window.c | 21 ++++++++++++++ 7 files changed, 140 insertions(+), 28 deletions(-)
diff --git a/dlls/win32u/class.c b/dlls/win32u/class.c index b35618d80c2..3d7aad89338 100644 --- a/dlls/win32u/class.c +++ b/dlls/win32u/class.c @@ -630,8 +630,9 @@ static const WCHAR real_class_id_str[][MAX_ATOM_LEN + 1] = { INT WINAPI NtUserGetClassName( HWND hwnd, BOOL real, UNICODE_STRING *name ) { const WCHAR *basename = NULL; - CLASS *class; + DWORD real_class_id = 0; int ret = 0; + WND *wnd;
TRACE( "%p %x %p\n", hwnd, real, name );
@@ -641,49 +642,48 @@ INT WINAPI NtUserGetClassName( HWND hwnd, BOOL real, UNICODE_STRING *name ) return 0; }
- if (!(class = get_class_ptr( hwnd, FALSE ))) return 0; + if (!(wnd = get_win_ptr( hwnd ))) + { + RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE ); + return 0; + }
- if (class == OBJ_OTHER_PROCESS) + if (wnd == WND_OTHER_PROCESS || wnd == WND_DESKTOP) { ATOM atom = 0;
- SERVER_START_REQ( set_class_info ) + SERVER_START_REQ( get_window_class_name ) { - req->window = wine_server_user_handle( hwnd ); - req->flags = 0; - req->extra_offset = -1; - req->extra_size = 0; + req->handle = wine_server_user_handle( hwnd ); if (!wine_server_call_err( req )) + { + real_class_id = reply->real_class_id; atom = reply->base_atom; + } } SERVER_END_REQ;
- return NtUserGetAtomName( atom, name ); + if (!real || (real && !real_class_id)) return NtUserGetAtomName( atom, name ); + wnd = NULL; } + else + real_class_id = wnd->real_class_id;
- if (real) + if (real && real_class_id) { - WND *wnd; - - if (!(wnd = get_win_ptr( hwnd ))) - { - RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE ); - goto exit; - } - - if (wnd->real_class_id) - basename = real_class_id_str[wnd->real_class_id - 1]; - release_win_ptr(wnd); + assert(real_class_id <= ARRAY_SIZE(real_class_id_str)); + basename = real_class_id_str[real_class_id - 1]; + } + else + { + assert(wnd); + basename = (const WCHAR *)wnd->class->basename; } - - if (!basename) - basename = (const WCHAR *)class->basename;
ret = min( name->MaximumLength / sizeof(WCHAR) - 1, lstrlenW(basename) ); if (ret) memcpy( name->Buffer, basename, ret * sizeof(WCHAR) ); name->Buffer[ret] = 0; -exit: - release_class_ptr( class ); + if (wnd) release_win_ptr( wnd ); return ret; }
diff --git a/dlls/win32u/window.c b/dlls/win32u/window.c index ae9a24125e5..c705be93c0d 100644 --- a/dlls/win32u/window.c +++ b/dlls/win32u/window.c @@ -4503,7 +4503,13 @@ static BOOL set_real_window_class_id( HWND hwnd, DWORD param )
if (!win->real_class_id) { - FIXME("Real class ID currently set in-process only.\n"); + SERVER_START_REQ( set_real_window_class_id ) + { + req->handle = wine_server_user_handle( hwnd ); + req->real_class_id = param; + wine_server_call( req ); + } + SERVER_END_REQ; win->real_class_id = param; } release_win_ptr( win ); diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index a392b532f4e..4fe1d20352b 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -5377,6 +5377,34 @@ struct set_window_layered_info_reply
+struct get_window_class_name_request +{ + struct request_header __header; + user_handle_t handle; +}; +struct get_window_class_name_reply +{ + struct reply_header __header; + atom_t base_atom; + unsigned int real_class_id; +}; + + + +struct set_real_window_class_id_request +{ + struct request_header __header; + user_handle_t handle; + unsigned int real_class_id; + char __pad_20[4]; +}; +struct set_real_window_class_id_reply +{ + struct reply_header __header; +}; + + + struct alloc_user_handle_request { struct request_header __header; @@ -5903,6 +5931,8 @@ enum request REQ_set_fd_eof_info, REQ_get_window_layered_info, REQ_set_window_layered_info, + REQ_get_window_class_name, + REQ_set_real_window_class_id, REQ_alloc_user_handle, REQ_free_user_handle, REQ_set_cursor, @@ -6194,6 +6224,8 @@ union generic_request struct set_fd_eof_info_request set_fd_eof_info_request; struct get_window_layered_info_request get_window_layered_info_request; struct set_window_layered_info_request set_window_layered_info_request; + struct get_window_class_name_request get_window_class_name_request; + struct set_real_window_class_id_request set_real_window_class_id_request; struct alloc_user_handle_request alloc_user_handle_request; struct free_user_handle_request free_user_handle_request; struct set_cursor_request set_cursor_request; @@ -6483,6 +6515,8 @@ union generic_reply struct set_fd_eof_info_reply set_fd_eof_info_reply; struct get_window_layered_info_reply get_window_layered_info_reply; struct set_window_layered_info_reply set_window_layered_info_reply; + struct get_window_class_name_reply get_window_class_name_reply; + struct set_real_window_class_id_reply set_real_window_class_id_reply; struct alloc_user_handle_reply alloc_user_handle_reply; struct free_user_handle_reply free_user_handle_reply; struct set_cursor_reply set_cursor_reply; @@ -6504,7 +6538,7 @@ union generic_reply
/* ### protocol_version begin ### */
-#define SERVER_PROTOCOL_VERSION 784 +#define SERVER_PROTOCOL_VERSION 785
/* ### protocol_version end ### */
diff --git a/server/protocol.def b/server/protocol.def index e9195df6b65..6654f0ba7aa 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -3733,6 +3733,22 @@ struct handle_info @END
+/* Get class name atom/real class ID for a window. */ +@REQ(get_window_class_name) + user_handle_t handle; /* handle to the window */ +@REPLY + atom_t base_atom; /* Base class atom. */ + unsigned int real_class_id; /* Real window class ID value. */ +@END + + +/* Set real window class ID value for a window. */ +@REQ(set_real_window_class_id) + user_handle_t handle; /* handle to the window */ + unsigned int real_class_id; /* Real window class ID value. */ +@END + + /* Allocate an arbitrary user handle */ @REQ(alloc_user_handle) @REPLY diff --git a/server/request.h b/server/request.h index d6043c5fdc3..042268c16ea 100644 --- a/server/request.h +++ b/server/request.h @@ -386,6 +386,8 @@ DECL_HANDLER(set_fd_name_info); DECL_HANDLER(set_fd_eof_info); DECL_HANDLER(get_window_layered_info); DECL_HANDLER(set_window_layered_info); +DECL_HANDLER(get_window_class_name); +DECL_HANDLER(set_real_window_class_id); DECL_HANDLER(alloc_user_handle); DECL_HANDLER(free_user_handle); DECL_HANDLER(set_cursor); @@ -676,6 +678,8 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] = (req_handler)req_set_fd_eof_info, (req_handler)req_get_window_layered_info, (req_handler)req_set_window_layered_info, + (req_handler)req_get_window_class_name, + (req_handler)req_set_real_window_class_id, (req_handler)req_alloc_user_handle, (req_handler)req_free_user_handle, (req_handler)req_set_cursor, @@ -2263,6 +2267,14 @@ C_ASSERT( FIELD_OFFSET(struct set_window_layered_info_request, color_key) == 16 C_ASSERT( FIELD_OFFSET(struct set_window_layered_info_request, alpha) == 20 ); C_ASSERT( FIELD_OFFSET(struct set_window_layered_info_request, flags) == 24 ); C_ASSERT( sizeof(struct set_window_layered_info_request) == 32 ); +C_ASSERT( FIELD_OFFSET(struct get_window_class_name_request, handle) == 12 ); +C_ASSERT( sizeof(struct get_window_class_name_request) == 16 ); +C_ASSERT( FIELD_OFFSET(struct get_window_class_name_reply, base_atom) == 8 ); +C_ASSERT( FIELD_OFFSET(struct get_window_class_name_reply, real_class_id) == 12 ); +C_ASSERT( sizeof(struct get_window_class_name_reply) == 16 ); +C_ASSERT( FIELD_OFFSET(struct set_real_window_class_id_request, handle) == 12 ); +C_ASSERT( FIELD_OFFSET(struct set_real_window_class_id_request, real_class_id) == 16 ); +C_ASSERT( sizeof(struct set_real_window_class_id_request) == 24 ); C_ASSERT( sizeof(struct alloc_user_handle_request) == 16 ); C_ASSERT( FIELD_OFFSET(struct alloc_user_handle_reply, handle) == 8 ); C_ASSERT( sizeof(struct alloc_user_handle_reply) == 16 ); diff --git a/server/trace.c b/server/trace.c index 55ccefa1746..9c7cb003bcc 100644 --- a/server/trace.c +++ b/server/trace.c @@ -4452,6 +4452,23 @@ static void dump_set_window_layered_info_request( const struct set_window_layere fprintf( stderr, ", flags=%08x", req->flags ); }
+static void dump_get_window_class_name_request( const struct get_window_class_name_request *req ) +{ + fprintf( stderr, " handle=%08x", req->handle ); +} + +static void dump_get_window_class_name_reply( const struct get_window_class_name_reply *req ) +{ + fprintf( stderr, " base_atom=%04x", req->base_atom ); + fprintf( stderr, ", real_class_id=%08x", req->real_class_id ); +} + +static void dump_set_real_window_class_id_request( const struct set_real_window_class_id_request *req ) +{ + fprintf( stderr, " handle=%08x", req->handle ); + fprintf( stderr, ", real_class_id=%08x", req->real_class_id ); +} + static void dump_alloc_user_handle_request( const struct alloc_user_handle_request *req ) { } @@ -4874,6 +4891,8 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_set_fd_eof_info_request, (dump_func)dump_get_window_layered_info_request, (dump_func)dump_set_window_layered_info_request, + (dump_func)dump_get_window_class_name_request, + (dump_func)dump_set_real_window_class_id_request, (dump_func)dump_alloc_user_handle_request, (dump_func)dump_free_user_handle_request, (dump_func)dump_set_cursor_request, @@ -5161,6 +5180,8 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { NULL, (dump_func)dump_get_window_layered_info_reply, NULL, + (dump_func)dump_get_window_class_name_reply, + NULL, (dump_func)dump_alloc_user_handle_reply, NULL, (dump_func)dump_set_cursor_reply, @@ -5448,6 +5469,8 @@ static const char * const req_names[REQ_NB_REQUESTS] = { "set_fd_eof_info", "get_window_layered_info", "set_window_layered_info", + "get_window_class_name", + "set_real_window_class_id", "alloc_user_handle", "free_user_handle", "set_cursor", diff --git a/server/window.c b/server/window.c index 242e93f303a..62e1086a90c 100644 --- a/server/window.c +++ b/server/window.c @@ -92,6 +92,7 @@ struct window int prop_inuse; /* number of in-use window properties */ int prop_alloc; /* number of allocated window properties */ struct property *properties; /* window properties array */ + unsigned int real_class_id; /* Real window class ID. */ int nb_extra_bytes; /* number of extra bytes */ char *extra_bytes; /* extra bytes storage */ }; @@ -582,6 +583,7 @@ static struct window *create_window( struct window *parent, struct window *owner win->prop_inuse = 0; win->prop_alloc = 0; win->properties = NULL; + win->real_class_id = 0; win->nb_extra_bytes = 0; win->extra_bytes = NULL; win->window_rect = win->visible_rect = win->surface_rect = win->client_rect = empty_rect; @@ -3010,3 +3012,22 @@ DECL_HANDLER(set_window_layered_info) } else set_win32_error( ERROR_INVALID_WINDOW_HANDLE ); } + +/* Get class name atom/real class ID for a window. */ +DECL_HANDLER(get_window_class_name) +{ + struct window *win = get_window( req->handle ); + + if (!win) return; + reply->base_atom = get_class_atom(win->class); + reply->real_class_id = win->real_class_id; +} + +/* Set real window class ID value for a window. */ +DECL_HANDLER(set_real_window_class_id) +{ + struct window *win = get_window( req->handle ); + + if (!win) return; + win->real_class_id = req->real_class_id; +}
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/user32/class.c | 12 ++++++++++-- dlls/user32/tests/class.c | 23 ++++++++++++----------- 2 files changed, 22 insertions(+), 13 deletions(-)
diff --git a/dlls/user32/class.c b/dlls/user32/class.c index 0cfdf552a5d..e2322ea2fc0 100644 --- a/dlls/user32/class.c +++ b/dlls/user32/class.c @@ -498,7 +498,14 @@ INT WINAPI GetClassNameW( HWND hwnd, LPWSTR buffer, INT count ) */ UINT WINAPI RealGetWindowClassA( HWND hwnd, LPSTR buffer, UINT count ) { - return GetClassNameA( hwnd, buffer, count ); + WCHAR tmpbuf[MAX_ATOM_LEN + 1]; + DWORD len; + + if (count <= 0) return 0; + if (!RealGetWindowClassW( hwnd, tmpbuf, ARRAY_SIZE( tmpbuf ))) return 0; + RtlUnicodeToMultiByteN( buffer, count - 1, &len, tmpbuf, lstrlenW(tmpbuf) * sizeof(WCHAR) ); + buffer[len] = 0; + return len; }
@@ -507,7 +514,8 @@ UINT WINAPI RealGetWindowClassA( HWND hwnd, LPSTR buffer, UINT count ) */ UINT WINAPI RealGetWindowClassW( HWND hwnd, LPWSTR buffer, UINT count ) { - return GetClassNameW( hwnd, buffer, count ); + UNICODE_STRING name = { .Buffer = buffer, .MaximumLength = count * sizeof(WCHAR) }; + return NtUserGetClassName( hwnd, TRUE, &name ); }
diff --git a/dlls/user32/tests/class.c b/dlls/user32/tests/class.c index 57e2dd19e3f..1d8d5ee06d6 100644 --- a/dlls/user32/tests/class.c +++ b/dlls/user32/tests/class.c @@ -1604,16 +1604,16 @@ struct real_class_test { };
static const struct real_class_test class_tests[] = { - { L"Button", "Button", L"Button", "Button", TRUE, TRUE }, - { L"ComboBox", "ComboBox", L"ComboBox", "ComboBox", TRUE, TRUE }, - { L"Edit", "Edit", L"Edit", "Edit", TRUE, TRUE }, - { L"ListBox", "ListBox", L"ListBox", "ListBox", TRUE, TRUE }, - { L"ScrollBar", "ScrollBar", L"ScrollBar", "ScrollBar", TRUE, TRUE }, - { L"Static", "Static", L"Static", "Static", TRUE, TRUE }, - { L"ComboLBox", "ComboLBox", L"ListBox", "ListBox", TRUE, TRUE }, - { L"MDIClient", "MDIClient", L"MDIClient", "MDIClient", TRUE, TRUE }, - { L"#32768", "#32768", L"#32768", "#32768", TRUE, TRUE }, - { L"#32770", "#32770", L"#32770", "#32770", TRUE, TRUE }, + { L"Button", "Button", L"Button", "Button", TRUE, FALSE }, + { L"ComboBox", "ComboBox", L"ComboBox", "ComboBox", TRUE, FALSE }, + { L"Edit", "Edit", L"Edit", "Edit", TRUE, FALSE }, + { L"ListBox", "ListBox", L"ListBox", "ListBox", TRUE, FALSE }, + { L"ScrollBar", "ScrollBar", L"ScrollBar", "ScrollBar", TRUE, FALSE }, + { L"Static", "Static", L"Static", "Static", TRUE, FALSE }, + { L"ComboLBox", "ComboLBox", L"ListBox", "ListBox", TRUE, FALSE }, + { L"MDIClient", "MDIClient", L"MDIClient", "MDIClient", TRUE, FALSE }, + { L"#32768", "#32768", L"#32768", "#32768", TRUE, FALSE }, + { L"#32770", "#32770", L"#32770", "#32770", TRUE, FALSE }, /* Not all built-in classes set real window class. */ { L"Message", "Message", SUPER_CLASS_NAME_W, SUPER_CLASS_NAME_A, FALSE, FALSE }, }; @@ -1623,7 +1623,8 @@ static const struct real_class_test comctl32_class_tests[] = { { L"ComboBox", "ComboBox", SUPER_CLASS_NAME_W, SUPER_CLASS_NAME_A, }, { L"Edit", "Edit", SUPER_CLASS_NAME_W, SUPER_CLASS_NAME_A, }, { L"ListBox", "ListBox", SUPER_CLASS_NAME_W, SUPER_CLASS_NAME_A, }, - { L"ScrollBar", "ScrollBar", SUPER_CLASS_NAME_W, SUPER_CLASS_NAME_A, }, + /* We don't have a comctl32 scroll control in Wine. */ + { L"ScrollBar", "ScrollBar", SUPER_CLASS_NAME_W, SUPER_CLASS_NAME_A, FALSE, TRUE }, { L"Static", "Static", SUPER_CLASS_NAME_W, SUPER_CLASS_NAME_A, }, { L"ComboLBox", "ComboLBox", SUPER_CLASS_NAME_W, SUPER_CLASS_NAME_A, }, };
V2: Remove accidental whitespace introduction.
Nikolay Sivov (@nsivov) commented about dlls/user32/tests/class.c:
- strcat(path, "comctl32_class.manifest");
- create_manifest_file(path, comctl32_manifest);
- context = create_test_actctx(path);
- ret = DeleteFileA(path);
- ok(ret, "Failed to delete manifest file, error %ld.\n", GetLastError());
- module = GetModuleHandleA("comctl32");
- ok(!module, "comctl32 already loaded\n");
- ret = ActivateActCtx(context, &cookie);
- ok(ret, "Failed to activate context.\n");
- module = GetModuleHandleA("comctl32");
- ok(!module, "comctl32 already loaded\n");
- test_real_class_names(comctl32_class_tests, ARRAY_SIZE(comctl32_class_tests));
This is testing comctl32 behavior, so it should be moved there.
Nikolay Sivov (@nsivov) commented about dlls/user32/tests/class.c:
+};
+static const struct real_class_test class_tests[] = {
- { L"Button", "Button", L"Button", "Button", TRUE, TRUE },
- { L"ComboBox", "ComboBox", L"ComboBox", "ComboBox", TRUE, TRUE },
- { L"Edit", "Edit", L"Edit", "Edit", TRUE, TRUE },
- { L"ListBox", "ListBox", L"ListBox", "ListBox", TRUE, TRUE },
- { L"ScrollBar", "ScrollBar", L"ScrollBar", "ScrollBar", TRUE, TRUE },
- { L"Static", "Static", L"Static", "Static", TRUE, TRUE },
- { L"ComboLBox", "ComboLBox", L"ListBox", "ListBox", TRUE, TRUE },
- { L"MDIClient", "MDIClient", L"MDIClient", "MDIClient", TRUE, TRUE },
- { L"#32768", "#32768", L"#32768", "#32768", TRUE, TRUE },
- { L"#32770", "#32770", L"#32770", "#32770", TRUE, TRUE },
- /* Not all built-in classes set real window class. */
- { L"Message", "Message", SUPER_CLASS_NAME_W, SUPER_CLASS_NAME_A, FALSE, FALSE },
+};
Why do you need to duplicate all of that? E.g. what's a reason to expect W-name being different from A-name?
Auto-generated changes should not be a part of a patch proposed for upstream.[^1] [^2]
[^1]: [Wineserver § Help to create a server request](https://wiki.winehq.org/Wineserver#Help_to_create_a_server_request), WineHQ wiki: "As with all generated files, the files generated by make_requests shouldn't be included in your patch. [^2]: [Re: [PATCH] server: Allow skipping debug handle retrieval in get_process_debug_info.](https://www.winehq.org/mailman3/hyperkitty/list/wine-devel@winehq.org/messag...), wine-devel Mailing List Archives: "Also, we do not include automatically generated changes (make_requests) in the patches, they are generated during upstream commit."
Jinoh Kang (@iamahuman) commented about dlls/win32u/window.c:
return TRUE;
}
+static BOOL set_real_window_class_id( HWND hwnd, DWORD param ) +{
- WND *win = get_win_ptr( hwnd );
- if (!win || win == WND_OTHER_PROCESS || win == WND_DESKTOP)
- {
ERR("Can't set real window class for a window belonging to another process\n");
return FALSE;
- }
This code path is not tested, so it's not clear if this behavior matches that of Windows (current and future versions).
You should either test that cross-process NtUserSetRealClassId always fails, or convert the ERR to FIXME: ```suggestion:-0+0 if (!win) return FALSE; if (win == WND_OTHER_PROCESS || win == WND_DESKTOP) { FIXME("Can't set real window class for a window belonging to another process\n"); return FALSE; } ```
Jinoh Kang (@iamahuman) commented about dlls/user32/scroll.c:
{ switch(message) {
- case WM_CREATE:
Do you have a reason for this particular choice of message (`WM_CREATE`)? How about `WM_NCCREATE`?
Esme Povirk (@madewokherd) commented about dlls/user32/tests/class.c:
- todo_wine_if(todo) ok(!lstrcmpW(real_class_name_w, exp_real_class_name_w), "got %s\n", debugstr_w(real_class_name_w));
- todo_wine_if(todo) ok(len == lstrlenW(exp_real_class_name_w), "got %ld, %s\n", len, debugstr_w(exp_real_class_name_w));
- len = RealGetWindowClassA(hwnd, real_class_name_a, ARRAY_SIZE(real_class_name_a));
- todo_wine_if(todo) ok(!strcmp(real_class_name_a, exp_real_class_name_a), "got %s\n", real_class_name_a);
- todo_wine_if(todo) ok(len == strlen(exp_real_class_name_a), "got %ld, %s\n", len, exp_real_class_name_a);
+}
+static void test_real_class_name(const struct real_class_test *class_test, BOOL wide_string, int idx) +{
- const CLIENTCREATESTRUCT client_cs = { NULL, 1 }; /* Needed for MDIClient. */
- HWND hwnd;
- int i;
- /*
* First pass doesn't pass messages through to the class window procedure,
What's the point of doing this first test multiple times? Isn't the API usage identical?
On Sat Oct 14 02:21:51 2023 +0000, Jinoh Kang wrote:
This code path is not tested, so it's not clear if this behavior matches that of Windows (current and future versions). You should either test that cross-process NtUserSetRealClassId always fails, or convert the ERR to FIXME:
if (!win) return FALSE; if (win == WND_OTHER_PROCESS || win == WND_DESKTOP) { FIXME("Can't set real window class for a window belonging to another process\n"); return FALSE; }
Depending on whether any of the builtin winprocs tolerate being called on another process's window, it may not be possible to test.
On Fri Oct 13 18:32:58 2023 +0000, Nikolay Sivov wrote:
Why do you need to duplicate all of that? E.g. what's a reason to expect W-name being different from A-name?
No reason in particular other than trying to be thorough. If it's acceptable to just test the A or W version of the functions I can do that instead.
On Mon Oct 16 12:04:39 2023 +0000, Connor McAdams wrote:
No reason in particular other than trying to be thorough. If it's acceptable to just test the A or W version of the functions I can do that instead.
You can convert them in the test function to test both.
On Sat Oct 14 16:04:38 2023 +0000, Esme Povirk wrote:
What's the point of doing this first test multiple times? Isn't the API usage identical?
I'm not sure which test in particular you mean. If you mean `test_real_class_name`, it's called for both A/W registered classes, and inside of `test_real_class_name` real class name is tested first without having messages passed through to the real class window procedure, and then with messages passed through.
Depending on whether any of the builtin winprocs tolerate being called on another process's window,
AFAIK the real-window-class changing behavior can be observed via mere `WM_NULL`, which is unlikely to rely on process-specific state.
it may not be possible to test.
You mean it may not be possible to test NtUserSetRealClassId *indirectly* via builtin winprocs? If so, what prevents us from calling it directly? `win32u:win32u` already has a few tests for class names.
On Mon Oct 16 12:06:49 2023 +0000, Nikolay Sivov wrote:
You can convert them in the test function to test both.
So IIUC, your suggestion is to just store either a wide string or an ascii string in this structure, and then convert the stored constant for testing?
On Mon Oct 16 12:54:55 2023 +0000, Connor McAdams wrote:
So IIUC, your suggestion is to just store either a wide string or an ascii string in this structure, and then convert the stored constant for testing?
Yes, I don't see what value is there for storing duplicates.
On Mon Oct 16 12:14:49 2023 +0000, Connor McAdams wrote:
I'm not sure which test in particular you mean. If you mean `test_real_class_name`, it's called for both A/W registered classes, and inside of `test_real_class_name` real class name is tested first without having messages passed through to the real class window procedure, and then with messages passed through.
The global variable `pass_msg_to_real_class_wndproc` is being used to pass state implicitly to the window procedure.
On Mon Oct 16 13:21:59 2023 +0000, Jinoh Kang wrote:
The global variable `pass_msg_to_real_class_wndproc` is being used to pass state implicitly to the window procedure. **EDIT**: Actually nevermind, this does not answer your question. Sorry.
Hm, now I understand what you mean.
In the `pass_msg_to_real_class_wndproc = 0` case, we're not really testing NtUserSetRealClassId at all. We're merely testing RealGetWindowClass against *slightly* different (mostly just `cbWndExtra` and `style`) classes, all with the same class name (`"SuperClass Test"`) as well as the same wndproc behavior (simply delegates to `DefWindowProcW()`, except for `WM_NCCREATE`).
The `pass_msg_to_real_class_wndproc = 0` case should simply be factored out of `test_real_class_name` and be tested separately, instead of being tested for every built-in class (which doesn't make a difference since its builtin wndproc is being ignored).
On Mon Oct 16 13:27:21 2023 +0000, Jinoh Kang wrote:
Hm, now I understand what you mean. In the `pass_msg_to_real_class_wndproc = 0` case, we're not really testing NtUserSetRealClassId at all. We're merely testing RealGetWindowClass against *slightly* different (mostly just `cbWndExtra` and `style`) classes, all with the same class name (`"SuperClass Test"`) as well as the same wndproc behavior (simply delegates to `DefWindowProcW()`, except for `WM_NCCREATE`). The `pass_msg_to_real_class_wndproc = 0` case should simply be factored out of `test_real_class_name` and be tested separately, instead of being tested for every built-in class (which doesn't make a difference since its builtin wndproc is being ignored).
In short,
```c test_real_class_name() { for (i = 0; i < 2; i++) { pass_msg_to_real_class_wndproc = i; if (!i) test_hwnd_real_class_name_a_w(hwnd, SUPER_CLASS_NAME_W, SUPER_CLASS_NAME_A); else test_hwnd_real_class_name_a_w(hwnd, class_test->real_class_name_w, class_test->real_class_name_a);
} }
test_real_class_names() { for (i = 0; i < tests_count; i++) test_real_class_name(); } ```
This should be just
```c test_real_class_name() { for (i = 0; i < 2; i++) { test_hwnd_real_class_name_a_w(hwnd, class_test->real_class_name_w, class_test->real_class_name_a);
} }
test_real_class_names() { pass_msg_to_real_class_wndproc = 0; test_hwnd_real_class_name_a_w(hwnd, SUPER_CLASS_NAME_W, SUPER_CLASS_NAME_A);
pass_msg_to_real_class_wndproc = 1; for (i = 0; i < tests_count; i++) { test_real_class_name(); } } ```
Note that the `pass_msg_to_real_class_wndproc = 0` case is being tested only once in the latter code snippet.
On Mon Oct 16 13:30:46 2023 +0000, Jinoh Kang wrote:
In short,
test_real_class_name() { for (i = 0; i < 2; i++) { pass_msg_to_real_class_wndproc = i; if (!i) test_hwnd_real_class_name_a_w(hwnd, SUPER_CLASS_NAME_W, SUPER_CLASS_NAME_A); else test_hwnd_real_class_name_a_w(hwnd, class_test->real_class_name_w, class_test->real_class_name_a); } } test_real_class_names() { for (i = 0; i < tests_count; i++) test_real_class_name(); }
This should be just
test_real_class_name() { for (i = 0; i < 2; i++) { test_hwnd_real_class_name_a_w(hwnd, class_test->real_class_name_w, class_test->real_class_name_a); } } test_real_class_names() { pass_msg_to_real_class_wndproc = 0; test_hwnd_real_class_name_a_w(hwnd, SUPER_CLASS_NAME_W, SUPER_CLASS_NAME_A); pass_msg_to_real_class_wndproc = 1; for (i = 0; i < tests_count; i++) { test_real_class_name(); } }
Note that the `pass_msg_to_real_class_wndproc = 0` case is being tested only once in the latter code snippet.
Ah, yeah that makes sense. I guess I was mainly going for a clear before->after case here, but doing the first case on each class is definitely redundant.
On Mon Oct 16 12:52:43 2023 +0000, Jinoh Kang wrote:
Depending on whether any of the builtin winprocs tolerate being called
on another process's window, AFAIK the real-window-class changing behavior can be observed via mere `WM_NULL`, which is unlikely to rely on process-specific state.
it may not be possible to test.
You mean it may not be possible to test NtUserSetRealClassId *indirectly* via builtin winprocs? If so, what prevents us from calling it directly? `win32u:win32u` already has a few tests for class names.
AFAICT it's a Wine-specific internal introduced by this MR and doesn't exist on Windows.