[PATCH 0/5] MR11012: win32u: Implement real class name for static window class.
From: Connor McAdams <cmcadams@codeweavers.com> --- dlls/user32/tests/class.c | 214 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 214 insertions(+) diff --git a/dlls/user32/tests/class.c b/dlls/user32/tests/class.c index 49bec91ff9c..791a3e51a60 100644 --- a/dlls/user32/tests/class.c +++ b/dlls/user32/tests/class.c @@ -50,6 +50,31 @@ static const BOOL is_win64 = (sizeof(void *) > sizeof(int)); +#define run_in_process( a ) run_in_process_( __FILE__, __LINE__, a ) +static void run_in_process_( const char *file, int line, const char *args ) +{ + char cmdline[MAX_PATH * 2], test[MAX_PATH], *tmp, **argv; + STARTUPINFOA startup = {.cb = sizeof(STARTUPINFOA)}; + PROCESS_INFORMATION info = {0}; + const char *name; + DWORD ret; + int argc; + + name = file; + if ((tmp = strrchr( name, '\\' ))) name = tmp; + if ((tmp = strrchr( name, '/' ))) name = tmp; + strcpy( test, name ); + if ((tmp = strrchr( test, '.' ))) *tmp = 0; + + argc = winetest_get_mainargs( &argv ); + sprintf( cmdline, "%s %s %s", argv[0], argc > 1 ? argv[1] : test, args ); + ret = CreateProcessA( NULL, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &info ); + ok_(file, line)( ret, "CreateProcessA failed, error %lu\n", GetLastError() ); + if (!ret) return; + + wait_child_process( &info ); +} + static const char comctl32_manifest[] = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n" "<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\" manifestVersion=\"1.0\">\n" @@ -2482,12 +2507,200 @@ void test_class_multithread(void) CloseHandle(thread_unregister); } +struct real_class_test +{ + const char *class_name; + const char *real_class_name; + BOOL set_by_wm_null; + BOOL set_by_wm_create; + BOOL set_by_wm_nccreate; + BOOL test_cross_process; + BOOL wine_todo; +}; + +static const struct real_class_test class_tests[] = +{ + { "Button", "Button", FALSE, FALSE, TRUE, TRUE, TRUE }, + { "ComboBox", "ComboBox", FALSE, TRUE, TRUE, TRUE, TRUE }, + { "Edit", "Edit", FALSE, FALSE, TRUE, TRUE, TRUE }, + { "ListBox", "ListBox", FALSE, TRUE, TRUE, TRUE, TRUE }, + { "ScrollBar", "ScrollBar", FALSE, TRUE, FALSE, TRUE, TRUE }, + { "Static", "Static", TRUE, TRUE, TRUE, TRUE, TRUE }, + { "ComboLBox", "ListBox", FALSE, TRUE, TRUE, TRUE, TRUE }, + { "MDIClient", "MDIClient", TRUE, TRUE, TRUE, TRUE, TRUE }, + { "#32768", "#32768", FALSE, FALSE, TRUE, TRUE, TRUE }, + { "#32770", "#32770", TRUE, TRUE, TRUE, TRUE, TRUE }, + /* Not all built-in classes set real window class. */ + { "Message", NULL, FALSE, FALSE, FALSE, FALSE, FALSE }, +}; + +static WNDPROC real_class_wndproc; +static UINT real_class_message; +static WNDCLASSA test_class; + +static LRESULT WINAPI test_real_class_wndproc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) +{ + if (msg == real_class_message) CallWindowProcA( real_class_wndproc, hwnd, msg, wparam, lparam ); + if (msg == WM_NCCREATE) return 1; + return 0; +} + +#define check_real_class_name( a, b, c ) check_real_class_name_( __LINE__, a, b, c ) +static void check_real_class_name_( int line, HWND hwnd, const char *expect, BOOL todo ) +{ + WCHAR expectW[256], nameW[256]; + char nameA[256]; + ULONG len; + + len = RealGetWindowClassA( hwnd, nameA, ARRAY_SIZE(nameA) ); + todo_wine_if( todo ) ok_(__FILE__, line)( !strcmp( nameA, expect ), "got %s\n", nameA ); + todo_wine_if( todo ) ok_(__FILE__, line)( len == strlen( expect ), "got %ld\n", len ); + + MultiByteToWideChar( CP_ACP, 0, expect, -1, expectW, ARRAY_SIZE(expectW)); + len = RealGetWindowClassW( hwnd, nameW, ARRAY_SIZE(nameW)); + todo_wine_if( todo ) ok_(__FILE__, line)( !wcscmp( nameW, expectW ), "got %s\n", debugstr_w(nameW)); + todo_wine_if( todo ) ok_(__FILE__, line)( len == wcslen( expectW ), "got %ld\n", len ); +} + +static void test_real_class_name_msg( UINT msg, const char *expect, BOOL cross_process, BOOL todo ) +{ + const CLIENTCREATESTRUCT client_cs = {NULL, 1}; /* Needed for MDIClient. */ + HWND hwnd; + + real_class_message = msg; + hwnd = CreateWindowA( test_class.lpszClassName, "test", WS_OVERLAPPED, 0, 0, 50, 50, 0, 0, 0, (void *)&client_cs ); + ok( !!hwnd, "got %p\n", hwnd ); + if (msg == WM_NULL) SendMessageA( hwnd, WM_NULL, 0, 0 ); + + check_real_class_name( hwnd, expect ? expect : test_class.lpszClassName, expect ? todo : FALSE ); + if (expect && cross_process) + { + char cmdline[MAX_PATH]; + sprintf( cmdline, "test_RealGetWindowClass %p %s %d", hwnd, expect, todo ); + run_in_process( cmdline ); + } + + DestroyWindow( hwnd ); +} + +static void test_RealGetWindowClass( void ) +{ + WCHAR class_name_w[256]; + char class_name[20]; + HWND hwnd; + UINT ret; + int i; + + hwnd = CreateWindowA( "Button", "test", BS_CHECKBOX | WS_POPUP, 0, 0, 50, 14, 0, 0, 0, NULL ); + ok( !!hwnd, "hwnd == NULL\n" ); + + /* Basic tests. */ + 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 ); + + /* Custom class, RealGetWindowClass behaves the same as GetClassName. */ + memset( &test_class, 0, sizeof(test_class) ); + test_class.lpfnWndProc = ClassTest_WndProc2; + test_class.hInstance = GetModuleHandleA( NULL ); + test_class.lpszClassName = "WineTest Class"; + RegisterClassA( &test_class ); + + hwnd = CreateWindowA( test_class.lpszClassName, "test", WS_OVERLAPPED, 0, 0, 50, 50, 0, 0, 0, NULL ); + ok( !!hwnd, "hwnd == NULL\n" ); + + check_real_class_name( hwnd, test_class.lpszClassName, FALSE ); + + DestroyWindow( hwnd ); + UnregisterClassA( test_class.lpszClassName, GetModuleHandleA( NULL ) ); + + for (i = 0; i < ARRAY_SIZE(class_tests); i++) + { + const struct real_class_test *class_test = &class_tests[i]; + BOOL test_cross_process = class_test->test_cross_process; + + memset( &test_class, 0, sizeof(test_class) ); + ret = GetClassInfoA( NULL, class_test->class_name, &test_class ); + ok( ret, "GetClassInfoA failed: %lu\n", GetLastError() ); + real_class_wndproc = test_class.lpfnWndProc; + test_class.lpfnWndProc = test_real_class_wndproc; + test_class.hInstance = GetModuleHandleA( NULL ); + test_class.lpszClassName = "WineTest Class"; + RegisterClassA( &test_class ); + + test_real_class_name_msg( WM_NULL, class_test->set_by_wm_null ? class_test->real_class_name : NULL, + test_cross_process, class_test->wine_todo ); + if (class_test->set_by_wm_null) test_cross_process = FALSE; + + test_real_class_name_msg( WM_NCCREATE, class_test->set_by_wm_nccreate ? class_test->real_class_name : NULL, + test_cross_process, class_test->wine_todo ); + if (class_test->set_by_wm_nccreate) test_cross_process = FALSE; + + /* + * If we pass WM_CREATE without WM_NCCREATE first to the Edit window + * procedure, it will trigger a divide by zero exception. Just skip this test. + */ + if (!!strcmp( class_test->class_name, "Edit" )) skip( "Skipping edit class\n"); + else test_real_class_name_msg( WM_CREATE, class_test->set_by_wm_create ? class_test->real_class_name : NULL, + test_cross_process, class_test->wine_todo ); + UnregisterClassA( test_class.lpszClassName, GetModuleHandleA( NULL ) ); + } + + real_class_wndproc = NULL; +} + +static void test_RealGetWindowClass_process( HWND hwnd, const char *expect, BOOL todo ) +{ + check_real_class_name( hwnd, expect, todo ); +} + START_TEST(class) { char **argv; HANDLE hInstance = GetModuleHandleA( NULL ); int argc = winetest_get_mainargs( &argv ); + if (argc >= 3 && !strcmp( argv[2], "test_RealGetWindowClass" )) + return test_RealGetWindowClass_process( UlongToHandle( strtol( argv[3], NULL, 16 ) ), argv[4], strtol( argv[5], NULL, 0 ) ); if (argc >= 3) { test_comctl32_class( argv[2] ); @@ -2519,6 +2732,7 @@ START_TEST(class) test_actctx_classes(); test_class_name(); test_class_multithread(); + test_RealGetWindowClass(); /* this test unregisters the Button class so it should be executed at the end */ test_instances(); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11012
From: Connor McAdams <cmcadams@codeweavers.com> --- dlls/comctl32/tests/misc.c | 51 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/dlls/comctl32/tests/misc.c b/dlls/comctl32/tests/misc.c index be31bedc848..83dc93eaed3 100644 --- a/dlls/comctl32/tests/misc.c +++ b/dlls/comctl32/tests/misc.c @@ -394,6 +394,42 @@ static void test_LoadIconWithScaleDown(void) FreeLibrary(hinst); } +static WNDPROC real_class_wndproc; +static const char *real_class_str; + +static LRESULT WINAPI test_real_class_wndproc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) +{ + LRESULT lr = 0; + + /* + * Passing through WM_GETTEXT to the WC_IPADDRESSA procedure will cause an + * access violation on Windows 7 64-bit. + */ + if ((msg == WM_GETTEXT) && !strcmp( real_class_str, WC_IPADDRESSA )) return lr; + + lr = CallWindowProcA( real_class_wndproc, hwnd, msg, wparam, lparam ); + if (msg == WM_NCCREATE) lr = 1; + + return lr; +} + +#define check_real_class_name( a, b ) check_real_class_name_( __LINE__, a, b ) +static void check_real_class_name_( int line, HWND hwnd, const char *expect ) +{ + WCHAR expectW[256], nameW[256]; + char nameA[256]; + ULONG len; + + len = RealGetWindowClassA( hwnd, nameA, ARRAY_SIZE(nameA) ); + ok_(__FILE__, line)( !strcmp( nameA, expect ), "got %s\n", nameA ); + ok_(__FILE__, line)( len == strlen( expect ), "got %ld\n", len ); + + MultiByteToWideChar( CP_ACP, 0, expect, -1, expectW, ARRAY_SIZE(expectW)); + len = RealGetWindowClassW( hwnd, nameW, ARRAY_SIZE(nameW)); + ok_(__FILE__, line)( !wcscmp( nameW, expectW ), "got %s\n", debugstr_w(nameW)); + ok_(__FILE__, line)( len == wcslen( expectW ), "got %ld\n", len ); +} + static void check_class( const char *name, int must_exist, UINT style, UINT ignore, BOOL v6, DWORD classnameidx, BOOL classnameidx_todo ) { WNDCLASSA wc; @@ -431,6 +467,21 @@ static void check_class( const char *name, int must_exist, UINT style, UINT igno name, objid, classnameidx); DestroyWindow(hwnd); + + real_class_wndproc = wc.lpfnWndProc; + real_class_str = name; + wc.lpfnWndProc = test_real_class_wndproc; + wc.hInstance = GetModuleHandleA( NULL ); + wc.lpszClassName = "WineTest Class"; + RegisterClassA( &wc ); + + hwnd = CreateWindowA( wc.lpszClassName, 0, 0, 0, 0, 0, 0, 0, NULL, GetModuleHandleA( NULL ), 0 ); + check_real_class_name( hwnd, wc.lpszClassName ); + + DestroyWindow( hwnd ); + UnregisterClassA( wc.lpszClassName, GetModuleHandleA( NULL ) ); + real_class_wndproc = NULL; + real_class_str = NULL; } else ok( !must_exist, "System class %s does not exist\n", name ); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11012
From: Connor McAdams <cmcadams@codeweavers.com> --- dlls/win32u/tests/win32u.c | 55 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/dlls/win32u/tests/win32u.c b/dlls/win32u/tests/win32u.c index 19c05a97835..59c2016347c 100644 --- a/dlls/win32u/tests/win32u.c +++ b/dlls/win32u/tests/win32u.c @@ -175,6 +175,16 @@ static void test_window_props(void) DestroyWindow( hwnd ); } +static WNDPROC real_class_wndproc; + +static LRESULT WINAPI test_real_class_wndproc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) +{ + LRESULT lr = 0; + if (real_class_wndproc) lr = CallWindowProcW( real_class_wndproc, hwnd, msg, wparam, lparam ); + if (msg == WM_NCCREATE) lr = 1; + return lr; +} + static void test_class(void) { struct pinned_atom @@ -416,6 +426,51 @@ 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"Static", &cls ); + ok( ret, "GetClassInfoW failed: %lu\n", GetLastError() ); + + real_class_wndproc = cls.lpfnWndProc; + cls.lpfnWndProc = test_real_class_wndproc; + cls.hInstance = GetModuleHandleW( NULL ); + cls.lpszClassName = L"WineTest Class"; + + class = RegisterClassW( &cls ); + ok( class, "RegisterClassW failed: %lu\n", GetLastError() ); + + hwnd = CreateWindowW( cls.lpszClassName, 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 Static. */ + memset( buf, 0xcc, sizeof(buf) ); + name.Buffer = buf; + name.Length = 0xdead; + name.MaximumLength = sizeof(buf); + ret = NtUserGetClassName( hwnd, TRUE, &name ); + todo_wine ok( ret == 6, "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"Static" ), "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 == 14, "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, cls.lpszClassName ), "buf = %s\n", debugstr_w(buf) ); + + DestroyWindow( hwnd ); + + ret = UnregisterClassW( cls.lpszClassName, GetModuleHandleW( NULL ) ); + ok( ret, "UnregisterClassW failed: %lu\n", GetLastError() ); + real_class_wndproc = NULL; + + cls.lpszClassName = L"#1"; class = RegisterClassW( &cls ); ok( class == 1, "RegisterClassW failed: %lu\n", GetLastError() ); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11012
From: Connor McAdams <cmcadams@codeweavers.com> Signed-off-by: Connor McAdams <cmcadams@codeweavers.com> --- dlls/user32/class.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/dlls/user32/class.c b/dlls/user32/class.c index eb28b03479a..8c80126c90b 100644 --- a/dlls/user32/class.c +++ b/dlls/user32/class.c @@ -501,7 +501,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; } @@ -510,7 +517,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 ); } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11012
From: Rémi Bernon <rbernon@codeweavers.com> --- dlls/user32/static.c | 3 +-- dlls/user32/tests/class.c | 2 +- dlls/win32u/class.c | 9 +++++++++ dlls/win32u/tests/win32u.c | 4 ++-- dlls/win32u/win32u_private.h | 1 + dlls/win32u/window.c | 13 +++++++++++++ server/protocol.def | 2 ++ server/window.c | 6 ++++-- 8 files changed, 33 insertions(+), 7 deletions(-) diff --git a/dlls/user32/static.c b/dlls/user32/static.c index f36c35bcec4..7cb138c0fd3 100644 --- a/dlls/user32/static.c +++ b/dlls/user32/static.c @@ -325,6 +325,7 @@ LRESULT StaticWndProc_common( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam LONG style = full_style & SS_TYPEMASK; if (!IsWindow( hwnd )) return 0; + NtUserSetWindowFNID( hwnd, MAKE_FNID(NTUSER_WNDPROC_STATIC) ); switch (uMsg) { @@ -399,8 +400,6 @@ LRESULT StaticWndProc_common( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam { CREATESTRUCTW *cs = (CREATESTRUCTW *)lParam; - NtUserSetWindowFNID( hwnd, MAKE_FNID(NTUSER_WNDPROC_STATIC) ); - if (full_style & SS_SUNKEN || style == SS_ETCHEDHORZ || style == SS_ETCHEDVERT) SetWindowLongW( hwnd, GWL_EXSTYLE, GetWindowLongW( hwnd, GWL_EXSTYLE ) | WS_EX_STATICEDGE ); diff --git a/dlls/user32/tests/class.c b/dlls/user32/tests/class.c index 791a3e51a60..70a51178937 100644 --- a/dlls/user32/tests/class.c +++ b/dlls/user32/tests/class.c @@ -2525,7 +2525,7 @@ static const struct real_class_test class_tests[] = { "Edit", "Edit", FALSE, FALSE, TRUE, TRUE, TRUE }, { "ListBox", "ListBox", FALSE, TRUE, TRUE, TRUE, TRUE }, { "ScrollBar", "ScrollBar", FALSE, TRUE, FALSE, TRUE, TRUE }, - { "Static", "Static", TRUE, TRUE, TRUE, TRUE, TRUE }, + { "Static", "Static", TRUE, TRUE, TRUE, TRUE, FALSE }, { "ComboLBox", "ListBox", FALSE, TRUE, TRUE, TRUE, TRUE }, { "MDIClient", "MDIClient", TRUE, TRUE, TRUE, TRUE, TRUE }, { "#32768", "#32768", FALSE, FALSE, TRUE, TRUE, TRUE }, diff --git a/dlls/win32u/class.c b/dlls/win32u/class.c index 1bde8453ace..708e0f04f0c 100644 --- a/dlls/win32u/class.c +++ b/dlls/win32u/class.c @@ -889,6 +889,8 @@ INT WINAPI NtUserGetClassName( HWND hwnd, BOOL real, UNICODE_STRING *name ) WCHAR buffer[MAX_ATOM_LEN]; NTSTATUS status; UINT len = 0; + ATOM atom; + WORD fnid; int ret; TRACE( "%p %x %p\n", hwnd, real, name ); @@ -899,6 +901,13 @@ INT WINAPI NtUserGetClassName( HWND hwnd, BOOL real, UNICODE_STRING *name ) return 0; } + if (real && (fnid = get_window_fnid( hwnd )) && (fnid & 0x7fff) < ARRAY_SIZE(builtin_classes)) + { + get_desktop_window(); /* create the desktop window to trigger builtin class registration */ + atom = builtin_classes[fnid & 0x7fff].atom; + return NtUserGetAtomName( atom, name ); + } + while ((status = get_shared_window_class( hwnd, &lock, &class_shm )) == STATUS_PENDING) { len = class_shm->name_len - class_shm->name_offset * sizeof(WCHAR); diff --git a/dlls/win32u/tests/win32u.c b/dlls/win32u/tests/win32u.c index 59c2016347c..4481ffa1b32 100644 --- a/dlls/win32u/tests/win32u.c +++ b/dlls/win32u/tests/win32u.c @@ -448,10 +448,10 @@ static void test_class(void) name.Length = 0xdead; name.MaximumLength = sizeof(buf); ret = NtUserGetClassName( hwnd, TRUE, &name ); - todo_wine ok( ret == 6, "NtUserGetClassName returned %lu\n", ret ); + ok( ret == 6, "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"Static" ), "buf = %s\n", debugstr_w(buf) ); + ok( !wcscmp( buf, L"Static" ), "buf = %s\n", debugstr_w(buf) ); /* Get normal class instead of real class. */ memset( buf, 0xcc, sizeof(buf) ); diff --git a/dlls/win32u/win32u_private.h b/dlls/win32u/win32u_private.h index bf3e871ae96..be303ae4d13 100644 --- a/dlls/win32u/win32u_private.h +++ b/dlls/win32u/win32u_private.h @@ -292,6 +292,7 @@ extern BOOL is_zoomed( HWND hwnd ); extern BOOL set_window_pixel_format( HWND hwnd, int format, BOOL internal ); extern int get_window_pixel_format( HWND hwnd ); extern DWORD get_window_long( HWND hwnd, INT offset ); +extern UINT get_window_fnid( HWND hwnd ); extern ULONG_PTR get_window_long_ptr( HWND hwnd, INT offset, BOOL ansi ); extern BOOL get_window_rect( HWND hwnd, RECT *rect, UINT dpi ); enum coords_relative; diff --git a/dlls/win32u/window.c b/dlls/win32u/window.c index 4d3cdd9fff4..4b53686dba1 100644 --- a/dlls/win32u/window.c +++ b/dlls/win32u/window.c @@ -1161,6 +1161,17 @@ BOOL is_zoomed( HWND hwnd ) return (get_window_long( hwnd, GWL_STYLE ) & WS_MAXIMIZE) != 0; } +UINT get_window_fnid( HWND hwnd ) +{ + struct object_lock lock = OBJECT_LOCK_INIT; + const window_shm_t *window_shm = NULL; + UINT status, fnid = 0; + + while ((status = get_shared_window( hwnd, &lock, &window_shm )) == STATUS_PENDING) + fnid = window_shm->fnid; + return status ? 0 : fnid; +} + static LONG_PTR get_window_long_size( HWND hwnd, INT offset, UINT size, BOOL ansi, BOOL internal ) { LONG_PTR retval = 0; @@ -1577,11 +1588,13 @@ BOOL WINAPI NtUserSetWindowFNID( HWND hwnd, WORD fnid ) RtlSetLastWin32Error( ERROR_INVALID_PARAMETER ); return FALSE; } + if (fnid == get_window_fnid( hwnd )) return TRUE; SERVER_START_REQ( set_window_fnid ) { req->handle = wine_server_user_handle( hwnd ); req->atom = get_builtin_class_atom( fnid & 0x7fff ); + req->fnid = fnid; ret = !wine_server_call_err( req ); } SERVER_END_REQ; diff --git a/server/protocol.def b/server/protocol.def index c24eb3d9e11..4f4f90c80e1 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -1067,6 +1067,7 @@ typedef volatile struct { struct obj_locator class; /* object locator for the window class shared object */ unsigned int dpi_context; /* DPI awareness context */ + unsigned int fnid; /* builtin class FNID, or 0 */ data_size_t private_size; /* length of private extra bytes range */ } window_shm_t; @@ -2698,6 +2699,7 @@ enum message_type @REQ(set_window_fnid) user_handle_t handle; /* handle to the window */ atom_t atom; /* class atom */ + unsigned int fnid; /* builtin class FNID */ @END diff --git a/server/window.c b/server/window.c index 9d0568130f8..3fa71fc3f1c 100644 --- a/server/window.c +++ b/server/window.c @@ -689,6 +689,7 @@ static struct window *create_window( struct window *parent, struct window *owner { shared->class = class_locator; shared->dpi_context = NTUSER_DPI_PER_MONITOR_AWARE; + shared->fnid = 0; shared->private_size = 0; } SHARED_WRITE_END; @@ -2285,12 +2286,13 @@ DECL_HANDLER(set_window_fnid) if (!(win = get_window( req->handle ))) return; if (is_desktop_window( win ) && win->thread != current) return set_error( STATUS_ACCESS_DENIED ); - if (win->shared->private_size) return set_error( STATUS_INVALID_PARAMETER ); + if (win->shared->fnid && win->shared->fnid != req->fnid) return set_error( STATUS_INVALID_PARAMETER ); if (!(class = grab_class( current->process, req->atom, 0, &extra_bytes, &class_locator ))) return; SHARED_WRITE_BEGIN( win->shared, window_shm_t ) { - shared->private_size = extra_bytes; + shared->fnid = req->fnid; + shared->private_size = extra_bytes; } SHARED_WRITE_END; release_class( class ); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11012
This merge request was approved by Rémi Bernon. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/11012
participants (3)
-
Connor McAdams -
Rémi Bernon -
Rémi Bernon (@rbernon)