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 | 2 ++ dlls/user32/listbox.c | 2 ++ 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, 96 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..4c011e5fad7 100644 --- a/dlls/user32/edit.c +++ b/dlls/user32/edit.c @@ -4926,8 +4926,10 @@ LRESULT EditWndProc_common( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, B MultiByteToWideChar(CP_ACP, 0, nameA, -1, nameW, countW); } 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..78b5789e5aa 100644 --- a/dlls/user32/listbox.c +++ b/dlls/user32/listbox.c @@ -2652,6 +2652,8 @@ 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, }, };