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