And introduce support for integral class names on server side.
-- v2: server: Support integral atom strings. win32u: Introduce helpers to check desktop and message class. user32/tests: Test window class versioned name with integer atom. user32/tests: Test window class menu name with integer atom. win32u/tests: Test window class name with integer atom. user32/tests: Cleanup window class versioning tests.
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/user32/tests/class.c | 369 ++++++++++++++++++++++++-------------- 1 file changed, 234 insertions(+), 135 deletions(-)
diff --git a/dlls/user32/tests/class.c b/dlls/user32/tests/class.c index 58a34e9bf59..537651f31a9 100644 --- a/dlls/user32/tests/class.c +++ b/dlls/user32/tests/class.c @@ -1452,156 +1452,255 @@ static void test_actctx_classes(void) "<windowClass>MyTestClass</windowClass>" "</file>" "</assembly>"; - static const char *testclass = "MyTestClass"; - WNDCLASSA wc; + const HINSTANCE hinst = GetModuleHandleW( 0 ); + WNDCLASSA tmp_wc, wc = + { + .lpfnWndProc = ClassTest_WndProc, + .hIcon = LoadIconW( 0, (LPCWSTR)IDI_APPLICATION ), + }; + char buf[64], path[MAX_PATH]; + ATOM class, tmp_class; + HWND hwnd, tmp_hwnd; ULONG_PTR cookie; HANDLE context; - BOOL ret; - ATOM class; - HINSTANCE hinst; - char buff[64]; - HWND hwnd, hwnd2; - char path[MAX_PATH]; - - GetTempPathA(ARRAY_SIZE(path), path); - strcat(path, "actctx_classes.manifest"); - - create_manifest_file(path, main_manifest); - context = create_test_actctx(path); - ret = DeleteFileA(path); - ok(ret, "Failed to delete manifest file, error %ld.\n", GetLastError()); - - ret = ActivateActCtx(context, &cookie); - ok(ret, "Failed to activate context.\n"); - - memset(&wc, 0, sizeof(wc)); - wc.lpfnWndProc = ClassTest_WndProc; - wc.hIcon = LoadIconW(0, (LPCWSTR)IDI_APPLICATION); - wc.lpszClassName = testclass; - - hinst = GetModuleHandleW(0); - - ret = GetClassInfoA(hinst, testclass, &wc); - ok(!ret, "Expected failure.\n"); - - class = RegisterClassA(&wc); - ok(class != 0, "Failed to register class.\n"); - - /* Class info is available by versioned and regular names. */ - ret = GetClassInfoA(hinst, testclass, &wc); - ok(ret, "Failed to get class info.\n"); - - hwnd = CreateWindowExA(0, testclass, "test", 0, 0, 0, 0, 0, 0, 0, hinst, 0); - ok(hwnd != NULL, "Failed to create a window.\n"); - - hwnd2 = FindWindowExA(NULL, NULL, "MyTestClass", NULL); - ok(hwnd2 == hwnd, "Failed to find test window.\n"); - - hwnd2 = FindWindowExA(NULL, NULL, "4.3.2.1!MyTestClass", NULL); - ok(hwnd2 == NULL, "Unexpected find result %p.\n", hwnd2); - - ret = GetClassNameA(hwnd, buff, sizeof(buff)); - ok(ret, "Failed to get class name.\n"); - ok(!strcmp(buff, testclass), "Unexpected class name.\n"); - - ret = GetClassInfoA(hinst, "4.3.2.1!MyTestClass", &wc); - ok(ret, "Failed to get class info.\n"); - - ret = UnregisterClassA(testclass, hinst); - ok(!ret, "Failed to unregister class.\n"); - - ret = DeactivateActCtx(0, cookie); - ok(ret, "Failed to deactivate context.\n"); - - ret = GetClassInfoA(hinst, testclass, &wc); - ok(!ret, "Unexpected ret val %d.\n", ret); - - ret = GetClassInfoA(hinst, "4.3.2.1!MyTestClass", &wc); - ok(ret, "Failed to get class info.\n"); - - ret = GetClassNameA(hwnd, buff, sizeof(buff)); - ok(ret, "Failed to get class name.\n"); - ok(!strcmp(buff, testclass), "Unexpected class name.\n"); - - DestroyWindow(hwnd); + UINT ret;
- hwnd = CreateWindowExA(0, "4.3.2.1!MyTestClass", "test", 0, 0, 0, 0, 0, 0, 0, hinst, 0); - ok(hwnd != NULL, "Failed to create a window.\n"); + GetTempPathA( ARRAY_SIZE(path), path ); + strcat( path, "actctx_classes.manifest" );
- hwnd2 = FindWindowExA(NULL, NULL, "MyTestClass", NULL); - ok(hwnd2 == hwnd, "Failed to find test window.\n"); + create_manifest_file( path, main_manifest ); + context = create_test_actctx( path ); + ret = DeleteFileA( path ); + ok( ret, "DeleteFileA failed, error %ld.\n", GetLastError() );
- hwnd2 = FindWindowExA(NULL, NULL, "4.3.2.1!MyTestClass", NULL); - ok(hwnd2 == NULL, "Unexpected find result %p.\n", hwnd2); - - DestroyWindow(hwnd); - - ret = UnregisterClassA("MyTestClass", hinst); - ok(!ret, "Unexpected ret value %d.\n", ret); - - ret = UnregisterClassA("4.3.2.1!MyTestClass", hinst); - ok(ret, "Failed to unregister class.\n"); - - /* Register versioned class without active context. */ - wc.lpszClassName = "4.3.2.1!MyTestClass"; - class = RegisterClassA(&wc); - ok(class != 0, "Failed to register class.\n"); - - ret = ActivateActCtx(context, &cookie); - ok(ret, "Failed to activate context.\n");
+ ret = ActivateActCtx( context, &cookie ); + ok( ret, "ActivateActCtx failed, error %lu\n", GetLastError() ); + ret = GetClassInfoA( hinst, "MyTestClass", &tmp_wc ); + ok( !ret, "GetClassInfoA succeeded\n" ); wc.lpszClassName = "MyTestClass"; - class = RegisterClassA(&wc); - ok(class == 0, "Expected failure.\n"); - - ret = DeactivateActCtx(0, cookie); - ok(ret, "Failed to deactivate context.\n"); - - ret = UnregisterClassA("4.3.2.1!MyTestClass", hinst); - ok(ret, "Failed to unregister class.\n"); - - /* Only versioned name is registered. */ - ret = ActivateActCtx(context, &cookie); - ok(ret, "Failed to activate context.\n"); - + class = RegisterClassA( &wc ); + ok( class != 0, "RegisterClassA failed, error %lu\n", GetLastError() ); + ret = DeactivateActCtx( 0, cookie ); + ok( ret, "DeactivateActCtx failed, error %lu\n", GetLastError() ); + + + /* UnregisterClassA is possible by atom, versioned, base names when context is active */ + ret = ActivateActCtx( context, &cookie ); + ok( ret, "ActivateActCtx failed, error %lu\n", GetLastError() ); + ret = UnregisterClassA( MAKEINTRESOURCEA( class ), hinst ); + ok( ret, "UnregisterClassA failed, error %lu\n", GetLastError() ); + class = RegisterClassA( &wc ); + ok( class != 0, "RegisterClassA failed, error %lu\n", GetLastError() ); + ret = UnregisterClassA( "MyTestClass", hinst ); + ok( ret, "UnregisterClassA failed, error %lu\n", GetLastError() ); + class = RegisterClassA( &wc ); + ok( class != 0, "RegisterClassA failed, error %lu\n", GetLastError() ); + ret = UnregisterClassA( "4.3.2.1!MyTestClass", hinst ); + ok( ret, "UnregisterClassA failed, error %lu\n", GetLastError() ); + class = RegisterClassA( &wc ); + ok( class != 0, "RegisterClassA failed, error %lu\n", GetLastError() ); + ret = DeactivateActCtx( 0, cookie ); + ok( ret, "DeactivateActCtx failed, error %lu\n", GetLastError() ); + + /* UnregisterClassA is possible by versioned name only when context isn't active */ + ret = UnregisterClassA( MAKEINTRESOURCEA( class ), hinst ); + todo_wine ok( !ret, "UnregisterClassA succeeded\n" ); + ret = UnregisterClassA( "MyTestClass", hinst ); + ok( !ret, "UnregisterClassA succeeded\n" ); + ret = UnregisterClassA( "4.3.2.1!MyTestClass", hinst ); + todo_wine ok( ret, "UnregisterClassA failed, error %lu\n", GetLastError() ); + + + /* registering versioned class while context isn't active */ + wc.lpszClassName = "4.3.2.1!MyTestClass"; + class = RegisterClassA( &wc ); + ok( class != 0, "RegisterClassA failed, error %lu\n", GetLastError() ); + ret = GetClassInfoA( hinst, MAKEINTRESOURCEA( class ), &tmp_wc ); + ok( ret, "GetClassInfoA failed, error %lu\n", GetLastError() ); + ret = GetClassInfoA( hinst, "MyTestClass", &tmp_wc ); + ok( !ret, "GetClassInfoA succeeded\n" ); + ret = GetClassInfoA( hinst, "4.3.2.1!MyTestClass", &tmp_wc ); + ok( ret, "GetClassInfoA failed, error %lu\n", GetLastError() ); + + ret = ActivateActCtx( context, &cookie ); + ok( ret, "ActivateActCtx failed, error %lu\n", GetLastError() ); + + /* GetClassInfoA now works with base name */ + ret = GetClassInfoA( hinst, MAKEINTRESOURCEA( class ), &tmp_wc ); + ok( ret, "GetClassInfoA failed, error %lu\n", GetLastError() ); + ret = GetClassInfoA( hinst, "MyTestClass", &tmp_wc ); + ok( ret, "GetClassInfoA failed, error %lu\n", GetLastError() ); + ret = GetClassInfoA( hinst, "4.3.2.1!MyTestClass", &tmp_wc ); + ok( ret, "GetClassInfoA failed, error %lu\n", GetLastError() ); + + /* prevents the class to be registered when context is active */ + tmp_class = RegisterClassA( &wc ); + ok( !tmp_class, "RegisterClassA succeeded, error %lu\n", GetLastError() ); wc.lpszClassName = "MyTestClass"; - class = RegisterClassA(&wc); - ok(class != 0, "Failed to register class\n"); - - ret = DeactivateActCtx(0, cookie); - ok(ret, "Failed to deactivate context.\n"); - - ret = GetClassInfoA(hinst, "MyTestClass", &wc); - ok(!ret, "Expected failure.\n"); - - ret = GetClassInfoA(hinst, "4.3.2.1!MyTestClass", &wc); - ok(ret, "Failed to get class info.\n"); - - ret = UnregisterClassA("4.3.2.1!MyTestClass", hinst); - ok(ret, "Failed to unregister class.\n"); + tmp_class = RegisterClassA( &wc ); + ok( !tmp_class, "RegisterClassA succeeded, error %lu\n", GetLastError() ); + + ret = GetClassInfoA( hinst, MAKEINTRESOURCEA( class ), &tmp_wc ); + ok( ret, "GetClassInfoA failed, error %lu\n", GetLastError() ); + ret = GetClassInfoA( hinst, "MyTestClass", &tmp_wc ); + ok( ret, "GetClassInfoA failed, error %lu\n", GetLastError() ); + ret = GetClassInfoA( hinst, "4.3.2.1!MyTestClass", &tmp_wc ); + ok( ret, "GetClassInfoA failed, error %lu\n", GetLastError() ); + + /* versioned class can be unregistered with its base name */ + ret = UnregisterClassA( "MyTestClass", hinst ); + ok( ret, "UnregisterClassA failed, error %lu\n", GetLastError() ); + ret = DeactivateActCtx( 0, cookie ); + ok( ret, "DeactivateActCtx failed, error %lu\n", GetLastError() ); + + + /* registering unversioned class before context is activated */ + class = RegisterClassA( &wc ); + ok( class != 0, "RegisterClassA failed, error %lu\n", GetLastError() ); + ret = GetClassInfoA( hinst, MAKEINTRESOURCEA( class ), &tmp_wc ); + ok( ret, "GetClassInfoA failed, error %lu\n", GetLastError() ); + ret = GetClassInfoA( hinst, "MyTestClass", &tmp_wc ); + ok( ret, "GetClassInfoA failed, error %lu\n", GetLastError() ); + ret = GetClassInfoA( hinst, "4.3.2.1!MyTestClass", &tmp_wc ); + ok( !ret, "GetClassInfoA succeeded\n" ); + + ret = ActivateActCtx( context, &cookie ); + ok( ret, "ActivateActCtx failed, error %lu\n", GetLastError() ); + + /* unversioned class is innaccessible now */ + ret = GetClassInfoA( hinst, MAKEINTRESOURCEA( class ), &tmp_wc ); + todo_wine ok( !ret, "GetClassInfoA succeeded\n" ); + ret = GetClassInfoA( hinst, "MyTestClass", &tmp_wc ); + ok( !ret, "GetClassInfoA succeeded\n" ); + ret = GetClassInfoA( hinst, "4.3.2.1!MyTestClass", &tmp_wc ); + ok( !ret, "GetClassInfoA succeeded\n" ); + + /* versioned class can be registered when context is active, returns same atom as unversioned */ + tmp_class = RegisterClassA( &wc ); + todo_wine ok( tmp_class == class, "RegisterClassA failed, error %lu\n", GetLastError() ); + + ret = GetClassInfoA( hinst, MAKEINTRESOURCEA( class ), &tmp_wc ); + ok( ret, "GetClassInfoA failed, error %lu\n", GetLastError() ); + ret = GetClassInfoA( hinst, "MyTestClass", &tmp_wc ); + ok( ret, "GetClassInfoA failed, error %lu\n", GetLastError() ); + ret = GetClassInfoA( hinst, "4.3.2.1!MyTestClass", &tmp_wc ); + ok( ret, "GetClassInfoA failed, error %lu\n", GetLastError() ); + + /* versioned class can be unregistered with its base name */ + ret = UnregisterClassA( "MyTestClass", hinst ); + ok( ret, "UnregisterClassA failed, error %lu\n", GetLastError() ); + /* unversioned class cannot be unregistered */ + ret = UnregisterClassA( "MyTestClass", hinst ); + ok( !ret, "UnregisterClassA succeeded\n" ); + ret = UnregisterClassA( MAKEINTRESOURCEA( class ), hinst ); + todo_wine ok( !ret, "UnregisterClassA succeeded\n" ); + ret = DeactivateActCtx( 0, cookie ); + ok( ret, "DeactivateActCtx failed, error %lu\n", GetLastError() ); + /* unversioned class can be unregistered now */ + ret = UnregisterClassA( MAKEINTRESOURCEA( class ), hinst ); + todo_wine ok( ret, "UnregisterClassA failed, error %lu\n", GetLastError() ); + + + /* class info is available by atom, versioned, base names when context is active */ + ret = ActivateActCtx( context, &cookie ); + ok( ret, "ActivateActCtx failed, error %lu\n", GetLastError() ); + class = RegisterClassA( &wc ); + ok( class != 0, "RegisterClassA failed, error %lu\n", GetLastError() ); + ret = GetClassInfoA( hinst, MAKEINTRESOURCEA( class ), &wc ); + ok( ret, "GetClassInfoA failed, error %lu\n", GetLastError() ); + ret = GetClassInfoA( hinst, "MyTestClass", &wc ); + ok( ret, "GetClassInfoA failed, error %lu\n", GetLastError() ); + ret = GetClassInfoA( hinst, "4.3.2.1!MyTestClass", &wc ); + ok( ret, "GetClassInfoA failed, error %lu\n", GetLastError() ); + + /* class info is available by versioned name only, if context isn't active. */ + ret = DeactivateActCtx( 0, cookie ); + ok( ret, "DeactivateActCtx failed, error %lu\n", GetLastError() ); + ret = GetClassInfoA( hinst, MAKEINTRESOURCEA( class ), &wc ); + todo_wine ok( !ret, "GetClassInfoA succeeded\n" ); + ret = GetClassInfoA( hinst, "MyTestClass", &wc ); + ok( !ret, "GetClassInfoA succeeded\n" ); + ret = GetClassInfoA( hinst, "4.3.2.1!MyTestClass", &wc ); + ok( ret, "GetClassInfoA failed, error %lu\n", GetLastError() ); + + + /* CreateWindow is allowed by atom, versioned, base names when context is active */ + ret = ActivateActCtx( context, &cookie ); + ok( ret, "ActivateActCtx failed, error %lu\n", GetLastError() ); + hwnd = CreateWindowExA( 0, MAKEINTRESOURCEA( class ), NULL, 0, 0, 0, 0, 0, 0, 0, hinst, 0 ); + ok( hwnd != NULL, "CreateWindowExA failed, error %lu\n", GetLastError() ); + ret = GetClassNameA( hwnd, buf, sizeof(buf) ); + ok( ret == 11, "GetClassNameA returned %d, error %lu\n", ret, GetLastError() ); + ok( !strcmp( buf, "MyTestClass" ), "GetClassNameA returned %s\n", debugstr_a(buf) ); + DestroyWindow( hwnd ); + hwnd = CreateWindowExA( 0, "MyTestClass", NULL, 0, 0, 0, 0, 0, 0, 0, hinst, 0 ); + ok( hwnd != NULL, "CreateWindowExA failed, error %lu\n", GetLastError() ); + ret = GetClassNameA( hwnd, buf, sizeof(buf) ); + ok( ret == 11, "GetClassNameA returned %d, error %lu\n", ret, GetLastError() ); + ok( !strcmp( buf, "MyTestClass" ), "GetClassNameA returned %s\n", debugstr_a(buf) ); + DestroyWindow( hwnd ); + hwnd = CreateWindowExA( 0, "4.3.2.1!MyTestClass", NULL, 0, 0, 0, 0, 0, 0, 0, hinst, 0 ); + ok( hwnd != NULL, "CreateWindowExA failed, error %lu\n", GetLastError() ); + ret = GetClassNameA( hwnd, buf, sizeof(buf) ); + ok( ret == 11, "GetClassNameA returned %d, error %lu\n", ret, GetLastError() ); + ok( !strcmp( buf, "MyTestClass" ), "GetClassNameA returned %s\n", debugstr_a(buf) ); + DestroyWindow( hwnd );
- /* Register regular name first, it's not considered when versioned name is registered. */ - wc.lpszClassName = "MyTestClass"; - class = RegisterClassA(&wc); - ok(class != 0, "Failed to register class.\n"); + /* CreateWindow is allowed by versioned name only when context isn't active */ + ret = DeactivateActCtx( 0, cookie ); + ok( ret, "DeactivateActCtx failed, error %lu\n", GetLastError() ); + hwnd = CreateWindowExA( 0, MAKEINTRESOURCEA( class ), NULL, 0, 0, 0, 0, 0, 0, 0, hinst, 0 ); + todo_wine ok( !hwnd, "CreateWindowExA succeeded\n" ); + if (hwnd) DestroyWindow( hwnd ); + hwnd = CreateWindowExA( 0, "MyTestClass", NULL, 0, 0, 0, 0, 0, 0, 0, hinst, 0 ); + ok( !hwnd, "CreateWindowExA succeeded\n" ); + hwnd = CreateWindowExA( 0, "4.3.2.1!MyTestClass", NULL, 0, 0, 0, 0, 0, 0, 0, hinst, 0 ); + ok( hwnd != NULL, "CreateWindowExA failed, error %lu\n", GetLastError() ); + ret = GetClassNameA( hwnd, buf, sizeof(buf) ); + ok( ret == 11, "GetClassNameA returned %d, error %lu\n", ret, GetLastError() ); + ok( !strcmp( buf, "MyTestClass" ), "GetClassNameA returned %s\n", debugstr_a(buf) ); + DestroyWindow( hwnd );
- ret = ActivateActCtx(context, &cookie); - ok(ret, "Failed to activate context.\n");
- wc.lpszClassName = "MyTestClass"; - class = RegisterClassA(&wc); - ok(class != 0, "Failed to register class.\n"); + /* FindWindow is allowed only by atom and base names */ + ret = ActivateActCtx( context, &cookie ); + ok( ret, "ActivateActCtx failed, error %lu\n", GetLastError() ); + hwnd = CreateWindowExA( 0, "4.3.2.1!MyTestClass", NULL, 0, 0, 0, 0, 0, 0, 0, hinst, 0 ); + ok( hwnd != NULL, "CreateWindowExA failed, error %lu\n", GetLastError() ); + tmp_hwnd = FindWindowExA( NULL, NULL, MAKEINTRESOURCEA( class ), NULL ); + todo_wine ok( tmp_hwnd == hwnd, "FindWindowExA returned %p, error %lu\n", tmp_hwnd, GetLastError() ); + tmp_hwnd = FindWindowExA( NULL, NULL, "MyTestClass", NULL ); + ok( tmp_hwnd == hwnd, "FindWindowExA returned %p, error %lu\n", tmp_hwnd, GetLastError() ); + tmp_hwnd = FindWindowExA(NULL, NULL, "4.3.2.1!MyTestClass", NULL); + ok( tmp_hwnd == NULL, "FindWindowExA returned %p, error %lu\n", tmp_hwnd, GetLastError() ); + + ret = DeactivateActCtx( 0, cookie ); + ok( ret, "DeactivateActCtx failed, error %lu\n", GetLastError() ); + + tmp_hwnd = FindWindowExA( NULL, NULL, MAKEINTRESOURCEA( class ), NULL ); + todo_wine ok( tmp_hwnd == hwnd, "FindWindowExA returned %p, error %lu\n", tmp_hwnd, GetLastError() ); + tmp_hwnd = FindWindowExA( NULL, NULL, "MyTestClass", NULL ); + ok( tmp_hwnd == hwnd, "FindWindowExA returned %p, error %lu\n", tmp_hwnd, GetLastError() ); + tmp_hwnd = FindWindowExA(NULL, NULL, "4.3.2.1!MyTestClass", NULL); + ok( tmp_hwnd == NULL, "FindWindowExA returned %p, error %lu\n", tmp_hwnd, GetLastError() ); + DestroyWindow( hwnd );
- ret = DeactivateActCtx(0, cookie); - ok(ret, "Failed to deactivate context.\n"); + hwnd = CreateWindowExA( 0, "4.3.2.1!MyTestClass", NULL, 0, 0, 0, 0, 0, 0, 0, hinst, 0 ); + ok( hwnd != NULL, "CreateWindowExA failed, error %lu\n", GetLastError() ); + tmp_hwnd = FindWindowExA( NULL, NULL, MAKEINTRESOURCEA( class ), NULL ); + todo_wine ok( tmp_hwnd == hwnd, "FindWindowExA returned %p, error %lu\n", tmp_hwnd, GetLastError() ); + tmp_hwnd = FindWindowExA( NULL, NULL, "MyTestClass", NULL ); + ok( tmp_hwnd == hwnd, "FindWindowExA returned %p, error %lu\n", tmp_hwnd, GetLastError() ); + tmp_hwnd = FindWindowExA( NULL, NULL, "4.3.2.1!MyTestClass", NULL ); + ok( tmp_hwnd == NULL, "FindWindowExA returned %p, error %lu\n", tmp_hwnd, GetLastError() ); + DestroyWindow( hwnd );
- ret = UnregisterClassA("4.3.2.1!MyTestClass", hinst); - ok(ret, "Failed to unregister class.\n"); + ret = UnregisterClassA( "4.3.2.1!MyTestClass", hinst ); + ok( ret, "UnregisterClassA failed, error %lu\n", GetLastError() );
- ret = UnregisterClassA("MyTestClass", hinst); - ok(ret, "Failed to unregister class.\n");
- ReleaseActCtx(context); + ReleaseActCtx( context ); }
static void test_uxtheme(void)
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/win32u/tests/win32u.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+)
diff --git a/dlls/win32u/tests/win32u.c b/dlls/win32u/tests/win32u.c index acbc5bc3034..10655dfbaf0 100644 --- a/dlls/win32u/tests/win32u.c +++ b/dlls/win32u/tests/win32u.c @@ -341,6 +341,29 @@ static void test_class(void) "NtUserGetAtomName returned %lx %lu\n", ret, GetLastError() ); ok( buf[0] == 0xcccc, "buf = %s\n", debugstr_w(buf) );
+ cls.lpszClassName = L"#1"; + class = RegisterClassW( &cls ); + ok( class == 1, "RegisterClassW failed: %lu\n", GetLastError() ); + + hwnd = CreateWindowW( MAKEINTRESOURCEW(1), NULL, WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL, + CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, 0, 0, NULL, 0 ); + ok( !!hwnd, "CreateWindowW failed: %lu\n", GetLastError() ); + + memset( buf, 0xcc, sizeof(buf) ); + name.Buffer = buf; + name.Length = 0xdead; + name.MaximumLength = 8; + ret = NtUserGetClassName( hwnd, FALSE, &name ); + ok( ret == 2, "NtUserGetClassName returned %lu\n", ret ); + ok( name.Length == 0xdead, "Length = %u\n", name.Length ); + ok( name.MaximumLength == 8, "MaximumLength = %u\n", name.MaximumLength ); + ok( !wcscmp( buf, L"#1" ), "buf = %s\n", debugstr_w(buf) ); + + DestroyWindow( hwnd ); + + ret = UnregisterClassW( L"#1", GetModuleHandleW(NULL) ); + ok( ret, "UnregisterClassW failed: %lu\n", GetLastError() ); + for (int i = 0; i < ARRAY_SIZE(global_atoms); i++) { winetest_push_context( "%#x: %s", global_atoms[i].atom, debugstr_w(global_atoms[i].name) );
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/user32/tests/class.c | 42 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+)
diff --git a/dlls/user32/tests/class.c b/dlls/user32/tests/class.c index 537651f31a9..b941abed88c 100644 --- a/dlls/user32/tests/class.c +++ b/dlls/user32/tests/class.c @@ -1795,8 +1795,50 @@ static void test_class_name(void) nameW = (const WCHAR *)GetClassLongPtrW(hwnd, GCLP_MENUNAME); ok(!wcscmp(nameW, L"nameW"), "unexpected class name %s\n", debugstr_w(nameW));
+ res = GetClassLongPtrA(hwnd, GCW_ATOM); + ok(res != 0, "unexpected class atom %#Ix\n", res); + SetLastError(0xdeadbeef); + res = SetClassWord(hwnd, GCW_ATOM, 2); + todo_wine ok(res == 0, "SetClassLongPtrA returned %#Ix\n", res); + todo_wine ok(GetLastError() == ERROR_INVALID_INDEX, "got error %lu\n", GetLastError()); + SetLastError(0xdeadbeef); + res = SetClassLongPtrA(hwnd, GCW_ATOM, 2); + todo_wine ok(res == 0, "SetClassLongPtrA returned %#Ix\n", res); + todo_wine ok(GetLastError() == ERROR_INVALID_PARAMETER, "got error %lu\n", GetLastError()); + SetLastError(0xdeadbeef); + DestroyWindow(hwnd); UnregisterClassW(class_name, hinst); + + wcex.lpszClassName = MAKEINTRESOURCEW(1); + wcex.lpszMenuName = MAKEINTRESOURCEW(2); + ok(RegisterClassExW(&wcex), "RegisterClassExW returned 0\n"); + hwnd = CreateWindowExW(0, L"#1", NULL, WS_OVERLAPPEDWINDOW, + 0, 0, 0, 0, NULL, NULL, hinst, 0); + ok(hwnd != NULL, "Window was not created\n"); + + res = GetClassLongPtrA(hwnd, GCW_ATOM); + ok(res == 1, "unexpected class atom %#Ix\n", res); + SetLastError(0xdeadbeef); + res = SetClassWord(hwnd, GCW_ATOM, 2); + todo_wine ok(res == 0, "SetClassLongPtrA returned %#Ix\n", res); + todo_wine ok(GetLastError() == ERROR_INVALID_INDEX, "got error %lu\n", GetLastError()); + SetLastError(0xdeadbeef); + res = SetClassWord(hwnd, GCW_ATOM, 1); + todo_wine ok(res == 0, "SetClassLongPtrA returned %#Ix\n", res); + todo_wine ok(GetLastError() == ERROR_INVALID_INDEX, "got error %lu\n", GetLastError()); + SetLastError(0xdeadbeef); + res = SetClassLongPtrA(hwnd, GCW_ATOM, 2); + todo_wine ok(res == 0, "SetClassLongPtrA returned %#Ix\n", res); + todo_wine ok(GetLastError() == ERROR_INVALID_PARAMETER, "got error %lu\n", GetLastError()); + + nameA = (const char *)GetClassLongPtrA(hwnd, GCLP_MENUNAME); + todo_wine ok(!nameA, "unexpected class name %s\n", debugstr_a(nameA)); + nameW = (const WCHAR *)GetClassLongPtrW(hwnd, GCLP_MENUNAME); + todo_wine ok(!nameW, "unexpected class name %s\n", debugstr_w(nameW)); + + DestroyWindow(hwnd); + UnregisterClassW(wcex.lpszClassName, hinst); }
START_TEST(class)
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/user32/tests/class.c | 233 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 233 insertions(+)
diff --git a/dlls/user32/tests/class.c b/dlls/user32/tests/class.c index b941abed88c..2249a35d622 100644 --- a/dlls/user32/tests/class.c +++ b/dlls/user32/tests/class.c @@ -1450,6 +1450,7 @@ static void test_actctx_classes(void) "<assemblyIdentity version="4.3.2.1" name="Wine.WndClass.Test" type="win32" />" "<file name="file.exe">" "<windowClass>MyTestClass</windowClass>" + "<windowClass>#1234</windowClass>" "</file>" "</assembly>"; const HINSTANCE hinst = GetModuleHandleW( 0 ); @@ -1700,6 +1701,238 @@ static void test_actctx_classes(void) ok( ret, "UnregisterClassA failed, error %lu\n", GetLastError() );
+ /* integral atom class can be registered by atom or string */ + ret = ActivateActCtx( context, &cookie ); + ok( ret, "ActivateActCtx failed, error %lu\n", GetLastError() ); + ret = GetClassInfoA( hinst, "#1234", &tmp_wc ); + ok( !ret, "GetClassInfoA succeeded\n" ); + wc.lpszClassName = MAKEINTRESOURCEA(1234); + class = RegisterClassA( &wc ); + ok( class == 1234, "RegisterClassA failed, error %lu\n", GetLastError() ); + ret = UnregisterClassA( MAKEINTRESOURCEA( class ), hinst ); + ok( ret, "UnregisterClassA failed, error %lu\n", GetLastError() ); + wc.lpszClassName = "#1234"; + class = RegisterClassA( &wc ); + todo_wine ok( class == 1234, "RegisterClassA returned %#x, error %lu\n", class, GetLastError() ); + ret = DeactivateActCtx( 0, cookie ); + ok( ret, "DeactivateActCtx failed, error %lu\n", GetLastError() ); + + + /* UnregisterClassA is possible by atom, versioned, base names when context is active */ + ret = ActivateActCtx( context, &cookie ); + ok( ret, "ActivateActCtx failed, error %lu\n", GetLastError() ); + ret = UnregisterClassA( MAKEINTRESOURCEA( class ), hinst ); + ok( ret, "UnregisterClassA failed, error %lu\n", GetLastError() ); + class = RegisterClassA( &wc ); + todo_wine ok( class == 1234, "RegisterClassA failed, error %lu\n", GetLastError() ); + ret = UnregisterClassA( "#1234", hinst ); + ok( ret, "UnregisterClassA failed, error %lu\n", GetLastError() ); + class = RegisterClassA( &wc ); + todo_wine ok( class == 1234, "RegisterClassA failed, error %lu\n", GetLastError() ); + ret = UnregisterClassA( "4.3.2.1!#1234", hinst ); + ok( ret, "UnregisterClassA failed, error %lu\n", GetLastError() ); + class = RegisterClassA( &wc ); + todo_wine ok( class == 1234, "RegisterClassA failed, error %lu\n", GetLastError() ); + ret = DeactivateActCtx( 0, cookie ); + ok( ret, "DeactivateActCtx failed, error %lu\n", GetLastError() ); + + /* UnregisterClassA is possible by versioned name only when context isn't active */ + ret = UnregisterClassA( MAKEINTRESOURCEA( class ), hinst ); + todo_wine ok( !ret, "UnregisterClassA succeeded\n" ); + ret = UnregisterClassA( "#1234", hinst ); + ok( !ret, "UnregisterClassA succeeded\n" ); + ret = UnregisterClassA( "4.3.2.1!#1234", hinst ); + todo_wine ok( ret, "UnregisterClassA failed, error %lu\n", GetLastError() ); + + + /* registering versioned class while context isn't active */ + wc.lpszClassName = "4.3.2.1!#1234"; + class = RegisterClassA( &wc ); + ok( class != 1234, "RegisterClassA failed, error %lu\n", GetLastError() ); + ret = GetClassInfoA( hinst, MAKEINTRESOURCEA( class ), &tmp_wc ); + ok( ret, "GetClassInfoA failed, error %lu\n", GetLastError() ); + ret = GetClassInfoA( hinst, "#1234", &tmp_wc ); + ok( !ret, "GetClassInfoA succeeded\n" ); + ret = GetClassInfoA( hinst, "4.3.2.1!#1234", &tmp_wc ); + ok( ret, "GetClassInfoA failed, error %lu\n", GetLastError() ); + + ret = ActivateActCtx( context, &cookie ); + ok( ret, "ActivateActCtx failed, error %lu\n", GetLastError() ); + + /* GetClassInfoA now works with base name */ + ret = GetClassInfoA( hinst, MAKEINTRESOURCEA( class ), &tmp_wc ); + ok( ret, "GetClassInfoA failed, error %lu\n", GetLastError() ); + ret = GetClassInfoA( hinst, "#1234", &tmp_wc ); + ok( ret, "GetClassInfoA failed, error %lu\n", GetLastError() ); + ret = GetClassInfoA( hinst, "4.3.2.1!#1234", &tmp_wc ); + ok( ret, "GetClassInfoA failed, error %lu\n", GetLastError() ); + + /* prevents the class to be registered when context is active */ + tmp_class = RegisterClassA( &wc ); + ok( !tmp_class, "RegisterClassA succeeded, error %lu\n", GetLastError() ); + wc.lpszClassName = "#1234"; + tmp_class = RegisterClassA( &wc ); + ok( !tmp_class, "RegisterClassA succeeded, error %lu\n", GetLastError() ); + + ret = GetClassInfoA( hinst, MAKEINTRESOURCEA( class ), &tmp_wc ); + ok( ret, "GetClassInfoA failed, error %lu\n", GetLastError() ); + ret = GetClassInfoA( hinst, "#1234", &tmp_wc ); + ok( ret, "GetClassInfoA failed, error %lu\n", GetLastError() ); + ret = GetClassInfoA( hinst, "4.3.2.1!#1234", &tmp_wc ); + ok( ret, "GetClassInfoA failed, error %lu\n", GetLastError() ); + + /* versioned class can be unregistered with its base name */ + ret = UnregisterClassA( "#1234", hinst ); + ok( ret, "UnregisterClassA failed, error %lu\n", GetLastError() ); + ret = DeactivateActCtx( 0, cookie ); + ok( ret, "DeactivateActCtx failed, error %lu\n", GetLastError() ); + + + /* registering unversioned class before context is activated */ + class = RegisterClassA( &wc ); + ok( class == 1234, "RegisterClassA failed, error %lu\n", GetLastError() ); + ret = GetClassInfoA( hinst, MAKEINTRESOURCEA( class ), &tmp_wc ); + ok( ret, "GetClassInfoA failed, error %lu\n", GetLastError() ); + ret = GetClassInfoA( hinst, "#1234", &tmp_wc ); + ok( ret, "GetClassInfoA failed, error %lu\n", GetLastError() ); + ret = GetClassInfoA( hinst, "4.3.2.1!#1234", &tmp_wc ); + ok( !ret, "GetClassInfoA succeeded\n" ); + + ret = ActivateActCtx( context, &cookie ); + ok( ret, "ActivateActCtx failed, error %lu\n", GetLastError() ); + + /* unversioned class is innaccessible now */ + ret = GetClassInfoA( hinst, MAKEINTRESOURCEA( class ), &tmp_wc ); + todo_wine ok( !ret, "GetClassInfoA succeeded\n" ); + ret = GetClassInfoA( hinst, "#1234", &tmp_wc ); + ok( !ret, "GetClassInfoA succeeded\n" ); + ret = GetClassInfoA( hinst, "4.3.2.1!#1234", &tmp_wc ); + ok( !ret, "GetClassInfoA succeeded\n" ); + + /* versioned class can be registered when context is active, returns same atom as unversioned */ + tmp_class = RegisterClassA( &wc ); + todo_wine ok( tmp_class == class, "RegisterClassA failed, error %lu\n", GetLastError() ); + + ret = GetClassInfoA( hinst, MAKEINTRESOURCEA( class ), &tmp_wc ); + ok( ret, "GetClassInfoA failed, error %lu\n", GetLastError() ); + ret = GetClassInfoA( hinst, "#1234", &tmp_wc ); + ok( ret, "GetClassInfoA failed, error %lu\n", GetLastError() ); + ret = GetClassInfoA( hinst, "4.3.2.1!#1234", &tmp_wc ); + ok( ret, "GetClassInfoA failed, error %lu\n", GetLastError() ); + + /* versioned class can be unregistered with its base name */ + ret = UnregisterClassA( "#1234", hinst ); + ok( ret, "UnregisterClassA failed, error %lu\n", GetLastError() ); + /* unversioned class cannot be unregistered */ + ret = UnregisterClassA( "#1234", hinst ); + ok( !ret, "UnregisterClassA succeeded\n" ); + ret = UnregisterClassA( MAKEINTRESOURCEA( class ), hinst ); + todo_wine ok( !ret, "UnregisterClassA succeeded\n" ); + ret = DeactivateActCtx( 0, cookie ); + ok( ret, "DeactivateActCtx failed, error %lu\n", GetLastError() ); + /* unversioned class can be unregistered now */ + ret = UnregisterClassA( MAKEINTRESOURCEA( class ), hinst ); + todo_wine ok( ret, "UnregisterClassA failed, error %lu\n", GetLastError() ); + + + /* class info is available by atom, versioned, base names when context is active */ + ret = ActivateActCtx( context, &cookie ); + ok( ret, "ActivateActCtx failed, error %lu\n", GetLastError() ); + class = RegisterClassA( &wc ); + todo_wine ok( class == 1234, "RegisterClassA failed, error %lu\n", GetLastError() ); + ret = GetClassInfoA( hinst, MAKEINTRESOURCEA( class ), &wc ); + ok( ret, "GetClassInfoA failed, error %lu\n", GetLastError() ); + ret = GetClassInfoA( hinst, "#1234", &wc ); + ok( ret, "GetClassInfoA failed, error %lu\n", GetLastError() ); + ret = GetClassInfoA( hinst, "4.3.2.1!#1234", &wc ); + ok( ret, "GetClassInfoA failed, error %lu\n", GetLastError() ); + + /* class info is available by versioned name only, if context isn't active. */ + ret = DeactivateActCtx( 0, cookie ); + ok( ret, "DeactivateActCtx failed, error %lu\n", GetLastError() ); + ret = GetClassInfoA( hinst, MAKEINTRESOURCEA( class ), &wc ); + todo_wine ok( !ret, "GetClassInfoA succeeded\n" ); + ret = GetClassInfoA( hinst, "#1234", &wc ); + ok( !ret, "GetClassInfoA succeeded\n" ); + ret = GetClassInfoA( hinst, "4.3.2.1!#1234", &wc ); + ok( ret, "GetClassInfoA failed, error %lu\n", GetLastError() ); + + + /* CreateWindow is allowed by atom, versioned, base names when context is active */ + ret = ActivateActCtx( context, &cookie ); + ok( ret, "ActivateActCtx failed, error %lu\n", GetLastError() ); + hwnd = CreateWindowExA( 0, MAKEINTRESOURCEA( class ), NULL, 0, 0, 0, 0, 0, 0, 0, hinst, 0 ); + ok( hwnd != NULL, "CreateWindowExA failed, error %lu\n", GetLastError() ); + ret = GetClassNameA( hwnd, buf, sizeof(buf) ); + ok( ret == 5, "GetClassNameA returned %d, error %lu\n", ret, GetLastError() ); + ok( !strcmp( buf, "#1234" ), "GetClassNameA returned %s\n", debugstr_a(buf) ); + DestroyWindow( hwnd ); + hwnd = CreateWindowExA( 0, "#1234", NULL, 0, 0, 0, 0, 0, 0, 0, hinst, 0 ); + ok( hwnd != NULL, "CreateWindowExA failed, error %lu\n", GetLastError() ); + ret = GetClassNameA( hwnd, buf, sizeof(buf) ); + ok( ret == 5, "GetClassNameA returned %d, error %lu\n", ret, GetLastError() ); + ok( !strcmp( buf, "#1234" ), "GetClassNameA returned %s\n", debugstr_a(buf) ); + DestroyWindow( hwnd ); + hwnd = CreateWindowExA( 0, "4.3.2.1!#1234", NULL, 0, 0, 0, 0, 0, 0, 0, hinst, 0 ); + ok( hwnd != NULL, "CreateWindowExA failed, error %lu\n", GetLastError() ); + ret = GetClassNameA( hwnd, buf, sizeof(buf) ); + ok( ret == 5, "GetClassNameA returned %d, error %lu\n", ret, GetLastError() ); + ok( !strcmp( buf, "#1234" ), "GetClassNameA returned %s\n", debugstr_a(buf) ); + DestroyWindow( hwnd ); + + /* CreateWindow is allowed by versioned name only when context isn't active */ + ret = DeactivateActCtx( 0, cookie ); + ok( ret, "DeactivateActCtx failed, error %lu\n", GetLastError() ); + hwnd = CreateWindowExA( 0, MAKEINTRESOURCEA( class ), NULL, 0, 0, 0, 0, 0, 0, 0, hinst, 0 ); + todo_wine ok( !hwnd, "CreateWindowExA succeeded\n" ); + if (hwnd) DestroyWindow( hwnd ); + hwnd = CreateWindowExA( 0, "#1234", NULL, 0, 0, 0, 0, 0, 0, 0, hinst, 0 ); + ok( !hwnd, "CreateWindowExA succeeded\n" ); + hwnd = CreateWindowExA( 0, "4.3.2.1!#1234", NULL, 0, 0, 0, 0, 0, 0, 0, hinst, 0 ); + ok( hwnd != NULL, "CreateWindowExA failed, error %lu\n", GetLastError() ); + ret = GetClassNameA( hwnd, buf, sizeof(buf) ); + ok( ret == 5, "GetClassNameA returned %d, error %lu\n", ret, GetLastError() ); + ok( !strcmp( buf, "#1234" ), "GetClassNameA returned %s\n", debugstr_a(buf) ); + DestroyWindow( hwnd ); + + + /* FindWindow is allowed only by atom and base names */ + ret = ActivateActCtx( context, &cookie ); + ok( ret, "ActivateActCtx failed, error %lu\n", GetLastError() ); + hwnd = CreateWindowExA( 0, "4.3.2.1!#1234", NULL, 0, 0, 0, 0, 0, 0, 0, hinst, 0 ); + ok( hwnd != NULL, "CreateWindowExA failed, error %lu\n", GetLastError() ); + tmp_hwnd = FindWindowExA( NULL, NULL, MAKEINTRESOURCEA( class ), NULL ); + todo_wine ok( tmp_hwnd == hwnd, "FindWindowExA returned %p, error %lu\n", tmp_hwnd, GetLastError() ); + tmp_hwnd = FindWindowExA( NULL, NULL, "#1234", NULL ); + todo_wine ok( tmp_hwnd == hwnd, "FindWindowExA returned %p, error %lu\n", tmp_hwnd, GetLastError() ); + tmp_hwnd = FindWindowExA(NULL, NULL, "4.3.2.1!#1234", NULL); + ok( tmp_hwnd == NULL, "FindWindowExA returned %p, error %lu\n", tmp_hwnd, GetLastError() ); + + ret = DeactivateActCtx( 0, cookie ); + ok( ret, "DeactivateActCtx failed, error %lu\n", GetLastError() ); + + tmp_hwnd = FindWindowExA( NULL, NULL, MAKEINTRESOURCEA( class ), NULL ); + todo_wine ok( tmp_hwnd == hwnd, "FindWindowExA returned %p, error %lu\n", tmp_hwnd, GetLastError() ); + tmp_hwnd = FindWindowExA( NULL, NULL, "#1234", NULL ); + todo_wine ok( tmp_hwnd == hwnd, "FindWindowExA returned %p, error %lu\n", tmp_hwnd, GetLastError() ); + tmp_hwnd = FindWindowExA(NULL, NULL, "4.3.2.1!#1234", NULL); + ok( tmp_hwnd == NULL, "FindWindowExA returned %p, error %lu\n", tmp_hwnd, GetLastError() ); + DestroyWindow( hwnd ); + + hwnd = CreateWindowExA( 0, "4.3.2.1!#1234", NULL, 0, 0, 0, 0, 0, 0, 0, hinst, 0 ); + ok( hwnd != NULL, "CreateWindowExA failed, error %lu\n", GetLastError() ); + tmp_hwnd = FindWindowExA( NULL, NULL, MAKEINTRESOURCEA( class ), NULL ); + todo_wine ok( tmp_hwnd == hwnd, "FindWindowExA returned %p, error %lu\n", tmp_hwnd, GetLastError() ); + tmp_hwnd = FindWindowExA( NULL, NULL, "#1234", NULL ); + todo_wine ok( tmp_hwnd == hwnd, "FindWindowExA returned %p, error %lu\n", tmp_hwnd, GetLastError() ); + tmp_hwnd = FindWindowExA( NULL, NULL, "4.3.2.1!#1234", NULL ); + ok( tmp_hwnd == NULL, "FindWindowExA returned %p, error %lu\n", tmp_hwnd, GetLastError() ); + DestroyWindow( hwnd ); + + ret = UnregisterClassA( "4.3.2.1!#1234", hinst ); + ok( ret, "UnregisterClassA failed, error %lu\n", GetLastError() ); + + ReleaseActCtx( context ); }
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/win32u/class.c | 22 +++++++++++++++++----- dlls/win32u/ntuser_private.h | 2 ++ dlls/win32u/window.c | 8 ++------ server/class.c | 2 +- server/user.h | 2 +- server/window.c | 2 +- 6 files changed, 24 insertions(+), 14 deletions(-)
diff --git a/dlls/win32u/class.c b/dlls/win32u/class.c index c62e920b77e..a34d14e6f8d 100644 --- a/dlls/win32u/class.c +++ b/dlls/win32u/class.c @@ -302,6 +302,22 @@ ATOM get_int_atom_value( UNICODE_STRING *name ) return ret; }
+BOOL is_desktop_class( UNICODE_STRING *name ) +{ + static const WCHAR desktopW[] = {'#','3','2','7','6','9'}; + ATOM atom; + if ((atom = get_int_atom_value( name ))) return atom == DESKTOP_CLASS_ATOM; + return name->Length == sizeof(desktopW) && !wcsnicmp( name->Buffer, desktopW, ARRAY_SIZE(desktopW) ); +} + +BOOL is_message_class( UNICODE_STRING *name ) +{ + static const WCHAR messageW[] = {'M','e','s','s','a','g','e'}; + ATOM atom; + if ((atom = get_int_atom_value( name ))) return FALSE; + return name->Length == sizeof(messageW) && !wcsnicmp( name->Buffer, messageW, ARRAY_SIZE(messageW) ); +} + /*********************************************************************** * get_class_ptr */ @@ -537,15 +553,11 @@ BOOL WINAPI NtUserUnregisterClass( UNICODE_STRING *name, HINSTANCE instance, ATOM WINAPI NtUserGetClassInfoEx( HINSTANCE instance, UNICODE_STRING *name, WNDCLASSEXW *wc, struct client_menu_name *menu_name, BOOL ansi ) { - static const WCHAR messageW[] = {'M','e','s','s','a','g','e'}; CLASS *class; ATOM atom;
/* create the desktop window to trigger builtin class registration */ - if (name->Buffer != (const WCHAR *)DESKTOP_CLASS_ATOM && - (IS_INTRESOURCE(name->Buffer) || name->Length != sizeof(messageW) || - wcsnicmp( name->Buffer, messageW, ARRAYSIZE(messageW) ))) - get_desktop_window(); + if (!is_desktop_class( name ) && !is_message_class( name )) get_desktop_window();
if (!(class = find_class( instance, name ))) return 0;
diff --git a/dlls/win32u/ntuser_private.h b/dlls/win32u/ntuser_private.h index 33b5cdc3b27..d2ad0660c3c 100644 --- a/dlls/win32u/ntuser_private.h +++ b/dlls/win32u/ntuser_private.h @@ -182,6 +182,8 @@ void get_winproc_params( struct win_proc_params *params, BOOL fixup_ansi_dst ); struct dce *get_class_dce( struct tagCLASS *class ); struct dce *set_class_dce( struct tagCLASS *class, struct dce *dce ); BOOL needs_ime_window( HWND hwnd ); +extern BOOL is_desktop_class( UNICODE_STRING *name ); +extern BOOL is_message_class( UNICODE_STRING *name ); extern void register_builtin_classes(void); extern void register_desktop_class(void);
diff --git a/dlls/win32u/window.c b/dlls/win32u/window.c index 08db9cab0bd..13ec161bdd3 100644 --- a/dlls/win32u/window.c +++ b/dlls/win32u/window.c @@ -5280,7 +5280,7 @@ static WND *create_window_handle( HWND parent, HWND owner, UNICODE_STRING *name, { struct ntuser_thread_info *thread_info = NtUserGetThreadInfo();
- if (name->Buffer == (const WCHAR *)DESKTOP_CLASS_ATOM) + if (is_desktop_class( name )) { if (!thread_info->top_window) thread_info->top_window = HandleToUlong( full_parent ? full_parent : handle ); else assert( full_parent == UlongToHandle( thread_info->top_window )); @@ -5410,8 +5410,6 @@ HWND WINAPI NtUserCreateWindowEx( DWORD ex_style, UNICODE_STRING *class_name, RECT surface_rect; WND *win;
- static const WCHAR messageW[] = {'M','e','s','s','a','g','e'}; - cs.lpCreateParams = params; cs.hInstance = instance ? instance : class_instance; cs.hMenu = menu; @@ -5454,9 +5452,7 @@ HWND WINAPI NtUserCreateWindowEx( DWORD ex_style, UNICODE_STRING *class_name, }
/* are we creating the desktop or HWND_MESSAGE parent itself? */ - if (class_name->Buffer != (LPCWSTR)DESKTOP_CLASS_ATOM && - (class_name->Length != sizeof(messageW) || - wcsnicmp( class_name->Buffer, messageW, ARRAYSIZE(messageW) ))) + if (!is_desktop_class( class_name ) && !is_message_class( class_name )) { if (get_process_layout() & LAYOUT_RTL) cs.dwExStyle |= WS_EX_LAYOUTRTL; parent = get_desktop_window(); diff --git a/server/class.c b/server/class.c index 3231f366b26..dc94c83f804 100644 --- a/server/class.c +++ b/server/class.c @@ -133,7 +133,7 @@ int is_desktop_class( struct window_class *class ) return (class->atom == DESKTOP_ATOM && !class->local); }
-int is_hwnd_message_class( struct window_class *class ) +int is_message_class( struct window_class *class ) { static const WCHAR messageW[] = {'M','e','s','s','a','g','e'}; static const struct unicode_str name = { messageW, sizeof(messageW) }; diff --git a/server/user.h b/server/user.h index ee0042b8755..0ebda06b49b 100644 --- a/server/user.h +++ b/server/user.h @@ -192,7 +192,7 @@ extern struct window_class *grab_class( struct process *process, atom_t atom, mod_handle_t instance, int *extra_bytes ); extern void release_class( struct window_class *class ); extern int is_desktop_class( struct window_class *class ); -extern int is_hwnd_message_class( struct window_class *class ); +extern int is_message_class( struct window_class *class ); extern int get_class_style( struct window_class *class ); extern atom_t get_class_atom( struct window_class *class ); extern client_ptr_t get_class_client_ptr( struct window_class *class ); diff --git a/server/window.c b/server/window.c index e2e9d5cc485..1c82d337676 100644 --- a/server/window.c +++ b/server/window.c @@ -627,7 +627,7 @@ static struct window *create_window( struct window *parent, struct window *owner { if (is_desktop_class( class )) parent = desktop->top_window; /* use existing desktop if any */ - else if (is_hwnd_message_class( class )) + else if (is_message_class( class )) /* use desktop window if message window is already created */ parent = desktop->msg_window ? desktop->top_window : NULL; else if (!(parent = desktop->top_window)) /* must already have a desktop then */
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/user32/tests/class.c | 6 +++--- server/atom.c | 26 ++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 3 deletions(-)
diff --git a/dlls/user32/tests/class.c b/dlls/user32/tests/class.c index 2249a35d622..f75891bcb3f 100644 --- a/dlls/user32/tests/class.c +++ b/dlls/user32/tests/class.c @@ -1904,7 +1904,7 @@ static void test_actctx_classes(void) tmp_hwnd = FindWindowExA( NULL, NULL, MAKEINTRESOURCEA( class ), NULL ); todo_wine ok( tmp_hwnd == hwnd, "FindWindowExA returned %p, error %lu\n", tmp_hwnd, GetLastError() ); tmp_hwnd = FindWindowExA( NULL, NULL, "#1234", NULL ); - todo_wine ok( tmp_hwnd == hwnd, "FindWindowExA returned %p, error %lu\n", tmp_hwnd, GetLastError() ); + ok( tmp_hwnd == hwnd, "FindWindowExA returned %p, error %lu\n", tmp_hwnd, GetLastError() ); tmp_hwnd = FindWindowExA(NULL, NULL, "4.3.2.1!#1234", NULL); ok( tmp_hwnd == NULL, "FindWindowExA returned %p, error %lu\n", tmp_hwnd, GetLastError() );
@@ -1914,7 +1914,7 @@ static void test_actctx_classes(void) tmp_hwnd = FindWindowExA( NULL, NULL, MAKEINTRESOURCEA( class ), NULL ); todo_wine ok( tmp_hwnd == hwnd, "FindWindowExA returned %p, error %lu\n", tmp_hwnd, GetLastError() ); tmp_hwnd = FindWindowExA( NULL, NULL, "#1234", NULL ); - todo_wine ok( tmp_hwnd == hwnd, "FindWindowExA returned %p, error %lu\n", tmp_hwnd, GetLastError() ); + ok( tmp_hwnd == hwnd, "FindWindowExA returned %p, error %lu\n", tmp_hwnd, GetLastError() ); tmp_hwnd = FindWindowExA(NULL, NULL, "4.3.2.1!#1234", NULL); ok( tmp_hwnd == NULL, "FindWindowExA returned %p, error %lu\n", tmp_hwnd, GetLastError() ); DestroyWindow( hwnd ); @@ -1924,7 +1924,7 @@ static void test_actctx_classes(void) tmp_hwnd = FindWindowExA( NULL, NULL, MAKEINTRESOURCEA( class ), NULL ); todo_wine ok( tmp_hwnd == hwnd, "FindWindowExA returned %p, error %lu\n", tmp_hwnd, GetLastError() ); tmp_hwnd = FindWindowExA( NULL, NULL, "#1234", NULL ); - todo_wine ok( tmp_hwnd == hwnd, "FindWindowExA returned %p, error %lu\n", tmp_hwnd, GetLastError() ); + ok( tmp_hwnd == hwnd, "FindWindowExA returned %p, error %lu\n", tmp_hwnd, GetLastError() ); tmp_hwnd = FindWindowExA( NULL, NULL, "4.3.2.1!#1234", NULL ); ok( tmp_hwnd == NULL, "FindWindowExA returned %p, error %lu\n", tmp_hwnd, GetLastError() ); DestroyWindow( hwnd ); diff --git a/server/atom.c b/server/atom.c index ff0799f5880..b63f4b75e10 100644 --- a/server/atom.c +++ b/server/atom.c @@ -198,6 +198,24 @@ static void atom_table_destroy( struct object *obj ) free( table->entries ); }
+static atom_t get_int_atom_value( const struct unicode_str *name ) +{ + const WCHAR *ptr = name->str; + const WCHAR *end = ptr + name->len / sizeof(WCHAR); + unsigned int ret = 0; + + if (IS_INTRESOURCE(ptr)) return LOWORD(ptr); + + if (*ptr++ != '#') return 0; + while (ptr < end) + { + if (*ptr < '0' || *ptr > '9') return 0; + ret = ret * 10 + *ptr++ - '0'; + if (ret > 0xffff) return 0; + } + return ret; +} + /* find an atom entry in its hash list */ static struct atom_entry *find_atom_entry( struct atom_table *table, const struct unicode_str *str, unsigned short hash ) @@ -228,6 +246,8 @@ static atom_t add_atom( struct atom_table *table, const struct unicode_str *str set_error( STATUS_INVALID_PARAMETER ); return 0; } + if ((atom = get_int_atom_value( str ))) return atom; + if ((entry = find_atom_entry( table, str, hash ))) /* exists already */ { entry->count++; @@ -273,6 +293,7 @@ static void delete_atom( struct atom_table *table, atom_t atom, int if_pinned ) static atom_t find_atom( struct atom_table *table, const struct unicode_str *str ) { struct atom_entry *entry; + atom_t atom;
if (!str->len) { @@ -284,6 +305,8 @@ static atom_t find_atom( struct atom_table *table, const struct unicode_str *str set_error( STATUS_INVALID_PARAMETER ); return 0; } + if ((atom = get_int_atom_value( str ))) return atom; + if (table && (entry = find_atom_entry( table, str, hash_strW( str->str, str->len, table->entries_count )))) return entry->atom; @@ -324,8 +347,11 @@ atom_t find_global_atom( struct winstation *winstation, const struct unicode_str { struct atom_table *table = get_global_table( winstation, 0 ); struct atom_entry *entry; + atom_t atom;
if (!str->len || str->len > MAX_ATOM_LEN || !table) return 0; + if ((atom = get_int_atom_value( str ))) return atom; + if ((entry = find_atom_entry( table, str, hash_strW( str->str, str->len, table->entries_count )))) return entry->atom; return 0;
Nit, `"#"` is being parsed as atom 0.
```suggestion:-5+0 do { if (*ptr < '0' || *ptr > '9') return 0; ret = ret * 10 + *ptr++ - '0'; if (ret > 0xffff) return 0; } while (ptr < end); ```
On Mon Jun 16 14:48:26 2025 +0000, Jinoh Kang wrote:
Nit, `"#"` is being parsed as atom 0.
do { if (*ptr < '0' || *ptr > '9') return 0; ret = ret * 10 + *ptr++ - '0'; if (ret > 0xffff) return 0; } while (ptr < end);
Well we return 0 in that case. Marking as not an issue.
On Mon Jun 16 14:49:44 2025 +0000, Jinoh Kang wrote:
Well we return 0 in that case. Marking as not an issue.
This is copied from other places, if the logic needs fixing it needs to be fixed everywhere.
On Mon Jun 16 14:55:48 2025 +0000, Rémi Bernon wrote:
This is copied from other places, if the logic needs fixing it needs to be fixed everywhere.
I already marked as not an issue, I guess this was unresolved by mistake? Anyway, resolving thread again.
Is there something wrong with this?
And introduce support for integral class names on server side.
Why is that necessary?
It would make moving things to the shared memory easier, without any need for special casing integral atoms.
Do you mean storing integral atoms as strings in shared memory? Is that really better?
Yes, and I think it generally makes the code simpler yes.
Basically there's no need to have two atoms for versioned class, one is enough and it is the base atom, as the versioned name doesn't seem to have a visible atom. Tests show that class atom also isn't allowed to change, to the contrary to what we currently allow.
Then versioned name still must be used used for actual class identification and would be stored in shared object as a string, regardless of whether base atom is integral or not (and tests show that integral atoms may still be used with versioned classes, which then require a string for the versioned class name).
Then, `NtUserGetClassName` as well as `find_class` can be implemented by directly reading the string from the class shared object without having to check for atom nature / versioned class nature.
OK, I see. I'd suggest to first add tests for things like "#01234" vs. "#1234" to make sure we use the right kind of string/number comparisons everywhere.