[PATCH 0/2] MR10277: user32: Make static control internal data private.
Fixes adding account in Quicken Classics. The application depends on GetWindowLong(static_hwnd, 0) returning 0. The implementation uses NtUserSetWindowFNID that is not compatible with Windows. According to information available on the Internet the function takes control id. Since the control id's are not documented and may change between Windows versions I have used the argument to pass private data range instead. Thanks to it we don't have to hardcode it on win32u side. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10277
From: Piotr Caban <piotr@codeweavers.com> --- dlls/user32/tests/static.c | 143 +++++++++++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) diff --git a/dlls/user32/tests/static.c b/dlls/user32/tests/static.c index a02cb249d49..9a2cf1629b3 100644 --- a/dlls/user32/tests/static.c +++ b/dlls/user32/tests/static.c @@ -329,10 +329,152 @@ static void test_STM_SETIMAGE(void) DeleteEnhMetaFile(emf); } +static WNDPROC static_proc; +static int test_data_off; +static LRESULT WINAPI static_wrapper_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + LRESULT ret; + LONG v; + + /* overwriting internal data works before static window proc is executed for the first time*/ + if (msg == WM_NCCREATE) + { + SetLastError(0xdeadbeef); + v = SetWindowLongA(hwnd, 0, 1); + ok(!v, "got %lx\n", v); + ok(GetLastError() == 0xdeadbeef, "GetLastError() = %ld\n", GetLastError()); + + v = GetWindowLongA(hwnd, 0); + ok(v == 1, "got %lx\n", v); + + SetWindowLongA(hwnd, 0, 0); + } + + SetWindowLongA(hwnd, test_data_off, 2); + v = GetWindowLongA(hwnd, test_data_off); + ok(v == 2, "got %lx\n", v); + + ret = static_proc(hwnd, msg, wparam, lparam); + + if (msg != WM_NCDESTROY) + { + SetLastError(0xdeadbeef); + v = SetWindowLongA(hwnd, 0, 3); + todo_wine_if(msg != WM_NCCREATE) ok(!v, "got %lx\n", v); + todo_wine ok(GetLastError() == ERROR_INVALID_INDEX, "GetLastError() = %ld\n", GetLastError()); + } + + v = SetWindowLongA(hwnd, test_data_off, 4); + ok(v == 2, "got %lx\n", v); + + SetLastError(0xdeadbeef); + v = GetWindowLongA(hwnd, 0); + todo_wine ok(!v, "got %lx\n", v); + todo_wine ok(GetLastError() == ERROR_INVALID_INDEX, "GetLastError() = %ld\n", GetLastError()); + + v = GetWindowLongA(hwnd, test_data_off); + ok(v == 4, "got %lx\n", v); + return ret; +} + +static void test_window_extra_data(char **argv) +{ + PROCESS_INFORMATION pi = {0}; + HWND hwnd = build_static(0); + STARTUPINFOA si = {0}; + char buffer[MAX_PATH]; + WNDCLASSW info; + LRESULT res; + MSG msg; + LONG v; + BOOL r; + + r = GetClassInfoW(NULL, L"static", &info); + ok(r, "GetClassInfo failed\n"); + ok(info.cbWndExtra >= sizeof(LONG), "info.cbWndExtra = %d\n", info.cbWndExtra); + + SendMessageA(hwnd, WM_SETFONT, 0xcafe, 0); + res = SendMessageA(hwnd, WM_GETFONT, 0, 0); + ok(res == 0xcafe, "got %Ix\n", res); + + SetLastError(0xdeadbeef); + v = GetWindowLongA(hwnd, 0); + todo_wine ok(!v, "got %lx\n", v); + todo_wine ok(GetLastError() == ERROR_INVALID_INDEX, "GetLastError() = %ld\n", GetLastError()); + + SetLastError(0xdeadbeef); + v = SetWindowLongA(hwnd, 0, 1); + todo_wine ok(!v, "got %lx\n", v); + todo_wine ok(GetLastError() == ERROR_INVALID_INDEX, "GetLastError() = %ld\n", GetLastError()); + + SetLastError(0xdeadbeef); + v = GetWindowLongW(hwnd, 0); + todo_wine ok(!v, "got %lx\n", v); + todo_wine ok(GetLastError() == ERROR_INVALID_INDEX, "GetLastError() = %ld\n", GetLastError()); + + si.cb = sizeof(si); + sprintf(buffer, "\"%s\" %s window_extra_data %p", argv[0], argv[1], hwnd); + r = CreateProcessA(argv[0], buffer, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); + ok(r, "CreateProcessA failed, error %lu.\n", GetLastError()); + while(GetMessageA(&msg, 0, 0, 0)) DispatchMessageA(&msg); + wait_child_process(&pi); + + DestroyWindow(hwnd); + + static_proc = info.lpfnWndProc; + test_data_off = info.cbWndExtra; + info.lpfnWndProc = static_wrapper_proc; + info.cbWndExtra += sizeof(LONG); + info.lpszClassName = L"static_wrapper"; + r = RegisterClassW(&info); + ok(r, "RegisterClass failed\n"); + + hwnd = CreateWindowA("static_wrapper", "", WS_CHILD, 0, 0, 100, 100, hMainWnd, NULL, NULL, NULL); + ok(hwnd != NULL, "CreateWindow failed\n"); + DestroyWindow(hwnd); + + r = UnregisterClassA("static_wrapper", NULL); + ok(r, "UnregisterClass failed\n"); +} + +static void test_window_extra_data_proc(char *arg) +{ + HWND hwnd; + LONG v; + + sscanf(arg, "%p", &hwnd); + + SetLastError(0xdeadbeef); + v = GetWindowLongA(hwnd, 0); + todo_wine ok(!v, "v = %ld\n", v); + todo_wine ok(GetLastError() == ERROR_INVALID_INDEX, "GetLastError() = %ld\n", GetLastError()); + + SetLastError(0xdeadbeef); + v = SetWindowLongA(hwnd, 0, 1); + todo_wine ok(!v, "v = %ld\n", v); + todo_wine ok(GetLastError() == 0xdeadbeef, "GetLastError() = %ld\n", GetLastError()); + + v = SetWindowLongA(hwnd, 0, 1); + todo_wine ok(!v, "v = %ld\n", v); + todo_wine ok(GetLastError() == 0xdeadbeef, "GetLastError() = %ld\n", GetLastError()); + + PostMessageA(hwnd, WM_QUIT, 0, 0); +} + START_TEST(static) { static const char szClassName[] = "testclass"; WNDCLASSEXA wndclass; + char **argv; + int argc; + + argc = winetest_get_mainargs(&argv); + if (argc > 2) + { + if (!lstrcmpA(argv[2], "window_extra_data")) + test_window_extra_data_proc(argv[3]); + return; + } wndclass.cbSize = sizeof(wndclass); wndclass.style = CS_HREDRAW | CS_VREDRAW; @@ -369,6 +511,7 @@ START_TEST(static) test_set_text(); test_set_image(); test_STM_SETIMAGE(); + test_window_extra_data(argv); DestroyWindow(hMainWnd); } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10277
From: Piotr Caban <piotr@codeweavers.com> --- dlls/user32/static.c | 25 +++++----- dlls/user32/tests/static.c | 28 +++++------ dlls/win32u/main.c | 5 ++ dlls/win32u/ntuser_private.h | 2 + dlls/win32u/win32syscalls.h | 5 +- dlls/win32u/win32u.spec | 2 +- dlls/win32u/window.c | 92 ++++++++++++++++++++++++++++++++---- dlls/wow64win/user.c | 8 ++++ include/ntuser.h | 34 +++++++++++++ server/window.c | 14 +++++- 10 files changed, 177 insertions(+), 38 deletions(-) diff --git a/dlls/user32/static.c b/dlls/user32/static.c index f3953f7610a..042255c0d88 100644 --- a/dlls/user32/static.c +++ b/dlls/user32/static.c @@ -94,7 +94,7 @@ static HICON STATIC_SetIcon( HWND hwnd, HICON hicon, DWORD style ) WARN("hicon != 0, but invalid\n"); return 0; } - prevIcon = (HICON)SetWindowLongPtrW( hwnd, HICON_GWL_OFFSET, (LONG_PTR)hicon ); + prevIcon = (HICON)NtUserSetPrivateData( hwnd, HICON_GWL_OFFSET, sizeof(hicon), (LONG_PTR)hicon ); if (hicon && !(style & SS_CENTERIMAGE) && !(style & SS_REALSIZECONTROL)) { /* Windows currently doesn't implement SS_RIGHTJUST */ @@ -127,7 +127,7 @@ static HBITMAP STATIC_SetBitmap( HWND hwnd, HBITMAP hBitmap, DWORD style ) WARN("hBitmap != 0, but it's not a bitmap\n"); return 0; } - hOldBitmap = (HBITMAP)SetWindowLongPtrW( hwnd, HICON_GWL_OFFSET, (LONG_PTR)hBitmap ); + hOldBitmap = (HBITMAP)NtUserSetPrivateData( hwnd, HICON_GWL_OFFSET, sizeof(hBitmap), (LONG_PTR)hBitmap ); if (hBitmap && !(style & SS_CENTERIMAGE) && !(style & SS_REALSIZECONTROL)) { BITMAP bm; @@ -162,7 +162,8 @@ static HENHMETAFILE STATIC_SetEnhMetaFile( HWND hwnd, HENHMETAFILE hEnhMetaFile, WARN("hEnhMetaFile != 0, but it's not an enhanced metafile\n"); return 0; } - return (HENHMETAFILE)SetWindowLongPtrW( hwnd, HICON_GWL_OFFSET, (LONG_PTR)hEnhMetaFile ); + return (HENHMETAFILE)NtUserSetPrivateData( hwnd, HICON_GWL_OFFSET, sizeof(hEnhMetaFile), + (LONG_PTR)hEnhMetaFile ); } /*********************************************************************** @@ -188,7 +189,7 @@ static HANDLE STATIC_GetImage( HWND hwnd, WPARAM wParam, DWORD style ) default: return NULL; } - return (HANDLE)GetWindowLongPtrW( hwnd, HICON_GWL_OFFSET ); + return (HANDLE)NtUserGetPrivateData( hwnd, HICON_GWL_OFFSET, sizeof(HANDLE) ); } /*********************************************************************** @@ -398,6 +399,8 @@ LRESULT StaticWndProc_common( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam { CREATESTRUCTW *cs = (CREATESTRUCTW *)lParam; + NtUserSetWindowFNID( hwnd, MAKE_FNID(0, HICON_GWL_OFFSET + sizeof(HANDLE)) ); + if (full_style & SS_SUNKEN || style == SS_ETCHEDHORZ || style == SS_ETCHEDVERT) SetWindowLongW( hwnd, GWL_EXSTYLE, GetWindowLongW( hwnd, GWL_EXSTYLE ) | WS_EX_STATICEDGE ); @@ -484,7 +487,7 @@ LRESULT StaticWndProc_common( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam case WM_SETFONT: if (hasTextStyle( full_style )) { - SetWindowLongPtrW( hwnd, HFONT_GWL_OFFSET, wParam ); + NtUserSetPrivateData( hwnd, HFONT_GWL_OFFSET, sizeof(HFONT), wParam ); if (LOWORD(lParam)) NtUserRedrawWindow( hwnd, NULL, 0, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW | RDW_ALLCHILDREN ); @@ -492,7 +495,7 @@ LRESULT StaticWndProc_common( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam break; case WM_GETFONT: - return GetWindowLongPtrW( hwnd, HFONT_GWL_OFFSET ); + return NtUserGetPrivateData( hwnd, HFONT_GWL_OFFSET, sizeof(HFONT) ); case WM_NCHITTEST: if (full_style & SS_NOTIFY) @@ -574,7 +577,7 @@ static void STATIC_PaintOwnerDrawfn( HWND hwnd, HDC hdc, HBRUSH hbrush, DWORD st dis.itemData = 0; GetClientRect( hwnd, &dis.rcItem ); - font = (HFONT)GetWindowLongPtrW( hwnd, HFONT_GWL_OFFSET ); + font = (HFONT)NtUserGetPrivateData( hwnd, HFONT_GWL_OFFSET, sizeof(HFONT) ); if (font) oldFont = SelectObject( hdc, font ); SendMessageW( GetParent(hwnd), WM_DRAWITEM, id, (LPARAM)&dis ); if (font) SelectObject( hdc, oldFont ); @@ -636,7 +639,7 @@ static void STATIC_PaintTextfn( HWND hwnd, HDC hdc, HBRUSH hbrush, DWORD style ) format |= DT_SINGLELINE | DT_WORD_ELLIPSIS; } - if ((hFont = (HFONT)GetWindowLongPtrW( hwnd, HFONT_GWL_OFFSET ))) + if ((hFont = (HFONT)NtUserGetPrivateData( hwnd, HFONT_GWL_OFFSET, sizeof(HFONT) ))) hOldFont = SelectObject( hdc, hFont ); if ((style & SS_TYPEMASK) != SS_SIMPLE) @@ -725,7 +728,7 @@ static void STATIC_PaintIconfn( HWND hwnd, HDC hdc, HBRUSH hbrush, DWORD style ) SIZE size; GetClientRect( hwnd, &rc ); - hIcon = (HICON)GetWindowLongPtrW( hwnd, HICON_GWL_OFFSET ); + hIcon = (HICON)NtUserGetPrivateData( hwnd, HICON_GWL_OFFSET, sizeof(hIcon) ); if (!hIcon || !get_icon_size( hIcon, &size )) { FillRect(hdc, &rc, hbrush); @@ -752,7 +755,7 @@ static void STATIC_PaintBitmapfn(HWND hwnd, HDC hdc, HBRUSH hbrush, DWORD style HDC hMemDC; HBITMAP hBitmap, oldbitmap; - if ((hBitmap = (HBITMAP)GetWindowLongPtrW( hwnd, HICON_GWL_OFFSET )) + if ((hBitmap = (HBITMAP)NtUserGetPrivateData( hwnd, HICON_GWL_OFFSET, sizeof(hBitmap) )) && (GetObjectType(hBitmap) == OBJ_BITMAP) && (hMemDC = CreateCompatibleDC( hdc ))) { @@ -792,7 +795,7 @@ static void STATIC_PaintEnhMetafn(HWND hwnd, HDC hdc, HBRUSH hbrush, DWORD style GetClientRect(hwnd, &rc); FillRect(hdc, &rc, hbrush); - if ((hEnhMetaFile = (HENHMETAFILE)GetWindowLongPtrW( hwnd, HICON_GWL_OFFSET ))) + if ((hEnhMetaFile = (HENHMETAFILE)NtUserGetPrivateData( hwnd, HICON_GWL_OFFSET, sizeof(hEnhMetaFile) ))) { /* The control's current font is not selected into the device context! */ diff --git a/dlls/user32/tests/static.c b/dlls/user32/tests/static.c index 9a2cf1629b3..382895cc7d9 100644 --- a/dlls/user32/tests/static.c +++ b/dlls/user32/tests/static.c @@ -360,8 +360,8 @@ static LRESULT WINAPI static_wrapper_proc(HWND hwnd, UINT msg, WPARAM wparam, LP { SetLastError(0xdeadbeef); v = SetWindowLongA(hwnd, 0, 3); - todo_wine_if(msg != WM_NCCREATE) ok(!v, "got %lx\n", v); - todo_wine ok(GetLastError() == ERROR_INVALID_INDEX, "GetLastError() = %ld\n", GetLastError()); + ok(!v, "got %lx\n", v); + ok(GetLastError() == ERROR_INVALID_INDEX, "GetLastError() = %ld\n", GetLastError()); } v = SetWindowLongA(hwnd, test_data_off, 4); @@ -369,8 +369,8 @@ static LRESULT WINAPI static_wrapper_proc(HWND hwnd, UINT msg, WPARAM wparam, LP SetLastError(0xdeadbeef); v = GetWindowLongA(hwnd, 0); - todo_wine ok(!v, "got %lx\n", v); - todo_wine ok(GetLastError() == ERROR_INVALID_INDEX, "GetLastError() = %ld\n", GetLastError()); + ok(!v, "got %lx\n", v); + ok(GetLastError() == ERROR_INVALID_INDEX, "GetLastError() = %ld\n", GetLastError()); v = GetWindowLongA(hwnd, test_data_off); ok(v == 4, "got %lx\n", v); @@ -399,18 +399,18 @@ static void test_window_extra_data(char **argv) SetLastError(0xdeadbeef); v = GetWindowLongA(hwnd, 0); - todo_wine ok(!v, "got %lx\n", v); - todo_wine ok(GetLastError() == ERROR_INVALID_INDEX, "GetLastError() = %ld\n", GetLastError()); + ok(!v, "got %lx\n", v); + ok(GetLastError() == ERROR_INVALID_INDEX, "GetLastError() = %ld\n", GetLastError()); SetLastError(0xdeadbeef); v = SetWindowLongA(hwnd, 0, 1); - todo_wine ok(!v, "got %lx\n", v); - todo_wine ok(GetLastError() == ERROR_INVALID_INDEX, "GetLastError() = %ld\n", GetLastError()); + ok(!v, "got %lx\n", v); + ok(GetLastError() == ERROR_INVALID_INDEX, "GetLastError() = %ld\n", GetLastError()); SetLastError(0xdeadbeef); v = GetWindowLongW(hwnd, 0); - todo_wine ok(!v, "got %lx\n", v); - todo_wine ok(GetLastError() == ERROR_INVALID_INDEX, "GetLastError() = %ld\n", GetLastError()); + ok(!v, "got %lx\n", v); + ok(GetLastError() == ERROR_INVALID_INDEX, "GetLastError() = %ld\n", GetLastError()); si.cb = sizeof(si); sprintf(buffer, "\"%s\" %s window_extra_data %p", argv[0], argv[1], hwnd); @@ -446,16 +446,16 @@ static void test_window_extra_data_proc(char *arg) SetLastError(0xdeadbeef); v = GetWindowLongA(hwnd, 0); - todo_wine ok(!v, "v = %ld\n", v); - todo_wine ok(GetLastError() == ERROR_INVALID_INDEX, "GetLastError() = %ld\n", GetLastError()); + ok(!v, "v = %ld\n", v); + ok(GetLastError() == ERROR_INVALID_INDEX, "GetLastError() = %ld\n", GetLastError()); SetLastError(0xdeadbeef); v = SetWindowLongA(hwnd, 0, 1); - todo_wine ok(!v, "v = %ld\n", v); + ok(!v, "v = %ld\n", v); todo_wine ok(GetLastError() == 0xdeadbeef, "GetLastError() = %ld\n", GetLastError()); v = SetWindowLongA(hwnd, 0, 1); - todo_wine ok(!v, "v = %ld\n", v); + ok(!v, "v = %ld\n", v); todo_wine ok(GetLastError() == 0xdeadbeef, "GetLastError() = %ld\n", GetLastError()); PostMessageA(hwnd, WM_QUIT, 0, 0); diff --git a/dlls/win32u/main.c b/dlls/win32u/main.c index aa47111ba72..73a6ae32448 100644 --- a/dlls/win32u/main.c +++ b/dlls/win32u/main.c @@ -2347,6 +2347,11 @@ BOOL SYSCALL_API NtUserSetWindowContextHelpId( HWND hwnd, DWORD id ) SYSCALL_FUNC( NtUserSetWindowContextHelpId ); } +BOOL SYSCALL_API NtUserSetWindowFNID( HWND hwnd, WORD fnid ) +{ + SYSCALL_FUNC( NtUserSetWindowFNID ); +} + LONG SYSCALL_API NtUserSetWindowLong( HWND hwnd, INT offset, LONG newval, BOOL ansi ) { SYSCALL_FUNC( NtUserSetWindowLong ); diff --git a/dlls/win32u/ntuser_private.h b/dlls/win32u/ntuser_private.h index ec6cf02ffb1..2f9bc9c2ccc 100644 --- a/dlls/win32u/ntuser_private.h +++ b/dlls/win32u/ntuser_private.h @@ -76,6 +76,8 @@ typedef struct tagWND int clip_clients; /* Has client surfaces that needs to be clipped out */ int cbWndExtra; /* class cbWndExtra at window creation */ DWORD_PTR userdata; /* User private data */ + int private_off; /* offset of private extra bytes range */ + int private_len; /* length of private extra bytes range */ DWORD wExtra[1]; /* Window extra bytes */ } WND; diff --git a/dlls/win32u/win32syscalls.h b/dlls/win32u/win32syscalls.h index 7826afd3fc3..aa0ceec4072 100644 --- a/dlls/win32u/win32syscalls.h +++ b/dlls/win32u/win32syscalls.h @@ -1441,7 +1441,7 @@ SYSCALL_ENTRY( 0x159d, NtUserSetWindowCompositionTransition, 0 ) \ SYSCALL_ENTRY( 0x159e, NtUserSetWindowContextHelpId, 8 ) \ SYSCALL_ENTRY( 0x159f, NtUserSetWindowDisplayAffinity, 0 ) \ - SYSCALL_ENTRY( 0x15a0, NtUserSetWindowFNID, 0 ) \ + SYSCALL_ENTRY( 0x15a0, NtUserSetWindowFNID, 8 ) \ SYSCALL_ENTRY( 0x15a1, NtUserSetWindowFeedbackSetting, 0 ) \ SYSCALL_ENTRY( 0x15a2, NtUserSetWindowGroup, 0 ) \ SYSCALL_ENTRY( 0x15a3, NtUserSetWindowLong, 16 ) \ @@ -2983,7 +2983,7 @@ SYSCALL_ENTRY( 0x159d, NtUserSetWindowCompositionTransition, 0 ) \ SYSCALL_ENTRY( 0x159e, NtUserSetWindowContextHelpId, 16 ) \ SYSCALL_ENTRY( 0x159f, NtUserSetWindowDisplayAffinity, 0 ) \ - SYSCALL_ENTRY( 0x15a0, NtUserSetWindowFNID, 0 ) \ + SYSCALL_ENTRY( 0x15a0, NtUserSetWindowFNID, 16 ) \ SYSCALL_ENTRY( 0x15a1, NtUserSetWindowFeedbackSetting, 0 ) \ SYSCALL_ENTRY( 0x15a2, NtUserSetWindowGroup, 0 ) \ SYSCALL_ENTRY( 0x15a3, NtUserSetWindowLong, 32 ) \ @@ -4086,7 +4086,6 @@ SYSCALL_STUB( NtUserSetWindowCompositionAttribute ) \ SYSCALL_STUB( NtUserSetWindowCompositionTransition ) \ SYSCALL_STUB( NtUserSetWindowDisplayAffinity ) \ - SYSCALL_STUB( NtUserSetWindowFNID ) \ SYSCALL_STUB( NtUserSetWindowFeedbackSetting ) \ SYSCALL_STUB( NtUserSetWindowGroup ) \ SYSCALL_STUB( NtUserSetWindowMessageCapability ) \ diff --git a/dlls/win32u/win32u.spec b/dlls/win32u/win32u.spec index 66e8541d915..2f657aa854e 100644 --- a/dlls/win32u/win32u.spec +++ b/dlls/win32u/win32u.spec @@ -1439,7 +1439,7 @@ @ stub -syscall NtUserSetWindowCompositionTransition @ stdcall -syscall NtUserSetWindowContextHelpId(long long) @ stub -syscall NtUserSetWindowDisplayAffinity -@ stub -syscall NtUserSetWindowFNID +@ stdcall -syscall NtUserSetWindowFNID(long long) @ stub -syscall NtUserSetWindowFeedbackSetting @ stub -syscall NtUserSetWindowGroup @ stdcall -syscall NtUserSetWindowLong(long long long long) diff --git a/dlls/win32u/window.c b/dlls/win32u/window.c index adbbc77937c..fc129df0785 100644 --- a/dlls/win32u/window.c +++ b/dlls/win32u/window.c @@ -1164,7 +1164,12 @@ BOOL is_zoomed( HWND hwnd ) return (get_window_long( hwnd, GWL_STYLE ) & WS_MAXIMIZE) != 0; } -static LONG_PTR get_window_long_size( HWND hwnd, INT offset, UINT size, BOOL ansi ) +static BOOL in_private_data_range( const WND *win, INT offset, UINT size ) +{ + return offset < win->private_off + win->private_len && offset + size >= win->private_off; +} + +static LONG_PTR get_window_long_size( HWND hwnd, INT offset, UINT size, BOOL ansi, BOOL internal ) { LONG_PTR retval = 0; WND *win; @@ -1227,7 +1232,8 @@ static LONG_PTR get_window_long_size( HWND hwnd, INT offset, UINT size, BOOL ans if (offset >= 0) { - if (offset > (int)(win->cbWndExtra - size)) + if (offset > (int)(win->cbWndExtra - size) || + (!internal && in_private_data_range( win, offset, size ))) { WARN("Invalid offset %d\n", offset ); release_win_ptr( win ); @@ -1268,13 +1274,13 @@ static LONG_PTR get_window_long_size( HWND hwnd, INT offset, UINT size, BOOL ans /* see GetWindowLongW */ DWORD get_window_long( HWND hwnd, INT offset ) { - return get_window_long_size( hwnd, offset, sizeof(LONG), FALSE ); + return get_window_long_size( hwnd, offset, sizeof(LONG), FALSE, FALSE ); } /* see GetWindowLongPtr */ ULONG_PTR get_window_long_ptr( HWND hwnd, INT offset, BOOL ansi ) { - return get_window_long_size( hwnd, offset, sizeof(LONG_PTR), ansi ); + return get_window_long_size( hwnd, offset, sizeof(LONG_PTR), ansi, FALSE ); } /* see GetWindowWord */ @@ -1285,7 +1291,7 @@ static WORD get_window_word( HWND hwnd, INT offset ) RtlSetLastWin32Error( ERROR_INVALID_INDEX ); return 0; } - return get_window_long_size( hwnd, offset, sizeof(WORD), TRUE ); + return get_window_long_size( hwnd, offset, sizeof(WORD), TRUE, FALSE ); } UINT set_window_style_bits( HWND hwnd, UINT set_bits, UINT clear_bits ) @@ -1385,7 +1391,8 @@ static HWND set_window_owner( HWND hwnd, HWND owner ) } /* Helper function for SetWindowLong(). */ -LONG_PTR set_window_long( HWND hwnd, INT offset, UINT size, LONG_PTR newval, BOOL ansi ) +static LONG_PTR set_window_long_internal( HWND hwnd, INT offset, UINT size, + LONG_PTR newval, BOOL ansi, BOOL internal ) { BOOL ok, made_visible = FALSE, layered = FALSE; LONG_PTR retval = 0; @@ -1484,7 +1491,8 @@ LONG_PTR set_window_long( HWND hwnd, INT offset, UINT size, LONG_PTR newval, BOO case GWLP_USERDATA: break; default: - if (offset < 0 || offset > (int)(win->cbWndExtra - size)) + if (offset < 0 || offset > (int)(win->cbWndExtra - size) || + (!internal && in_private_data_range( win, offset, size ))) { WARN("Invalid offset %d\n", offset ); release_win_ptr( win ); @@ -1567,6 +1575,62 @@ LONG_PTR set_window_long( HWND hwnd, INT offset, UINT size, LONG_PTR newval, BOO return retval; } +LONG_PTR set_window_long( HWND hwnd, INT offset, UINT size, LONG_PTR newval, BOOL ansi ) +{ + return set_window_long_internal( hwnd, offset, size, newval, ansi, FALSE ); +} + +/********************************************************************** + * NtUserSetWindowFNID (win32u.@) + * + * fnid parameter not compatible with Windows. + */ +BOOL WINAPI NtUserSetWindowFNID( HWND hwnd, WORD fnid ) +{ + int off = FNID_OFF(fnid); + int len = FNID_LEN(fnid); + WND *win; + BOOL ret; + + TRACE( "%p %x\n", hwnd, fnid ); + + if (!(win = get_win_ptr( hwnd ))) + { + RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE ); + return FALSE; + } + + if (win == WND_DESKTOP || win == WND_OTHER_PROCESS) + { + RtlSetLastWin32Error( ERROR_ACCESS_DENIED ); + return FALSE; + } + + if (win->private_len) + { + ret = win->private_off == off && win->private_len == len; + + release_win_ptr( win ); + if (!ret) RtlSetLastWin32Error( ERROR_INVALID_PARAMETER ); + return ret; + } + + SERVER_START_REQ( set_window_info ) + { + req->handle = wine_server_user_handle( hwnd ); + req->offset = GWLP_FNID_INTERNAL; + req->new_info = fnid; + if ((ret = !wine_server_call_err( req ))) + { + win->private_off = off; + win->private_len = len; + } + } + SERVER_END_REQ; + release_win_ptr( win ); + return ret; +} + /********************************************************************** * NtUserSetWindowWord (win32u.@) */ @@ -6126,7 +6190,7 @@ ULONG_PTR WINAPI NtUserCallHwndParam( HWND hwnd, DWORD_PTR param, DWORD code ) return get_window_info( hwnd, (WINDOWINFO *)param ); case NtUserCallHwndParam_GetWindowLongA: - return get_window_long_size( hwnd, param, sizeof(LONG), TRUE ); + return get_window_long_size( hwnd, param, sizeof(LONG), TRUE, FALSE ); case NtUserCallHwndParam_GetWindowLongW: return get_window_long( hwnd, param ); @@ -6211,6 +6275,18 @@ ULONG_PTR WINAPI NtUserCallHwndParam( HWND hwnd, DWORD_PTR param, DWORD code ) return set_raw_window_pos( hwnd, params->rect, params->flags, params->internal ); } + case NtUserCallHwndParam_GetPrivateData: + { + struct get_private_data_params *params = (void *)param; + return get_window_long_size( hwnd, params->offset, params->size, FALSE, TRUE ); + } + + case NtUserCallHwndParam_SetPrivateData: + { + struct set_private_data_params *params = (void *)param; + return set_window_long_internal( hwnd, params->offset, params->size, params->value, FALSE, TRUE ); + } + default: FIXME( "invalid code %u\n", code ); return 0; diff --git a/dlls/wow64win/user.c b/dlls/wow64win/user.c index 6bbb1fba508..0a570d79ff7 100644 --- a/dlls/wow64win/user.c +++ b/dlls/wow64win/user.c @@ -4622,6 +4622,14 @@ NTSTATUS WINAPI wow64_NtUserSetWindowContextHelpId( UINT *args ) return NtUserSetWindowContextHelpId( hwnd, id ); } +NTSTATUS WINAPI wow64_NtUserSetWindowFNID( UINT *args ) +{ + HWND hwnd = get_handle( &args ); + WORD fnid = get_ulong( &args ); + + return NtUserSetWindowFNID( hwnd, fnid ); +} + NTSTATUS WINAPI wow64_NtUserSetWindowLong( UINT *args ) { HWND hwnd = get_handle( &args ); diff --git a/include/ntuser.h b/include/ntuser.h index ce1b18fdfd9..31674c76858 100644 --- a/include/ntuser.h +++ b/include/ntuser.h @@ -667,6 +667,12 @@ enum wine_internal_message #define IMN_WINE_SET_OPEN_STATUS 0x000f #define IMN_WINE_SET_COMP_STRING 0x0010 +/* not compatible with Windows */ +#define GWLP_FNID_INTERNAL (-1000) +#define MAKE_FNID(off, len) (((off) << 8) + len) +#define FNID_OFF(fnid) ((fnid) >> 8) +#define FNID_LEN(fnid) ((fnid) & 0xff) + /* builtin IME driver calls */ enum wine_ime_call { @@ -1017,6 +1023,7 @@ W32KAPI HWND WINAPI NtUserSetTaskmanWindow( HWND hwnd ); W32KAPI BOOL WINAPI NtUserSetThreadDesktop( HDESK handle ); W32KAPI UINT_PTR WINAPI NtUserSetTimer( HWND hwnd, UINT_PTR id, UINT timeout, TIMERPROC proc, ULONG tolerance ); W32KAPI BOOL WINAPI NtUserSetWindowContextHelpId( HWND hwnd, DWORD id ); +W32KAPI BOOL WINAPI NtUserSetWindowFNID( HWND hwnd, WORD fnid ); W32KAPI LONG WINAPI NtUserSetWindowLong( HWND hwnd, INT offset, LONG newval, BOOL ansi ); W32KAPI LONG_PTR WINAPI NtUserSetWindowLongPtr( HWND hwnd, INT offset, LONG_PTR newval, BOOL ansi ); W32KAPI BOOL WINAPI NtUserSetWindowPlacement( HWND hwnd, const WINDOWPLACEMENT *wpl ); @@ -1422,6 +1429,8 @@ enum NtUserCallHwndParam_ExposeWindowSurface, NtUserCallHwndParam_GetWinMonitorDpi, NtUserCallHwndParam_SetRawWindowPos, + NtUserCallHwndParam_GetPrivateData, + NtUserCallHwndParam_SetPrivateData, }; struct get_window_rects_params @@ -1648,4 +1657,29 @@ static inline BOOL NtUserSetRawWindowPos( HWND hwnd, RECT rect, UINT flags, BOOL return NtUserCallHwndParam( hwnd, (UINT_PTR)¶ms, NtUserCallHwndParam_SetRawWindowPos ); } +struct get_private_data_params +{ + UINT offset; + UINT size; +}; + +static inline LONG_PTR NtUserGetPrivateData( HWND hwnd, UINT offset, UINT size ) +{ + struct get_private_data_params params = { .offset = offset, .size = size }; + return NtUserCallHwndParam( hwnd, (UINT_PTR)¶ms, NtUserCallHwndParam_GetPrivateData ); +} + +struct set_private_data_params +{ + UINT offset; + UINT size; + LONG_PTR value; +}; + +static inline LONG_PTR NtUserSetPrivateData( HWND hwnd, UINT offset, UINT size, LONG_PTR value ) +{ + struct set_private_data_params params = { .offset = offset, .size = size, .value = value }; + return NtUserCallHwndParam( hwnd, (UINT_PTR)¶ms, NtUserCallHwndParam_SetPrivateData ); +} + #endif /* _NTUSER_ */ diff --git a/server/window.c b/server/window.c index f4ce0dece3c..f222b5c2a59 100644 --- a/server/window.c +++ b/server/window.c @@ -92,6 +92,8 @@ 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 */ + int private_off; /* offset of private extra bytes range */ + int private_len; /* length of private extra bytes range */ int nb_extra_bytes; /* number of extra bytes */ char *extra_bytes; /* extra bytes storage */ window_shm_t *shared; /* window in session shared memory */ @@ -2273,6 +2275,11 @@ DECL_HANDLER(create_window) reply->class_ptr = get_class_client_ptr( win->class ); } +static BOOL in_private_data_range( const struct window *win, int offset, int size ) +{ + return offset < win->private_off + win->private_len && offset + size >= win->private_off; +} + /* set the parent of a window */ DECL_HANDLER(set_parent) @@ -2396,7 +2403,8 @@ DECL_HANDLER(get_window_info) case GWLP_USERDATA: reply->info = win->user_data; break; default: if (req->size > sizeof(reply->info) || req->offset < 0 || - req->offset > win->nb_extra_bytes - (int)req->size) + req->offset > win->nb_extra_bytes - (int)req->size || + in_private_data_range( win, req->offset, req->size )) { set_win32_error( ERROR_INVALID_INDEX ); break; @@ -2463,6 +2471,10 @@ DECL_HANDLER(set_window_info) reply->old_info = win->user_data; win->user_data = req->new_info; break; + case GWLP_FNID_INTERNAL: + win->private_off = FNID_OFF(req->new_info); + win->private_len = FNID_LEN(req->new_info); + break; default: if (req->size > sizeof(req->new_info) || req->offset < 0 || req->offset > win->nb_extra_bytes - (int)req->size) -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10277
I forgot to mention that similar user32 internal data protection should be added to other builtin controls. I've started by adding it only to static control. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10277#note_131620
Jacek Caban (@jacek) commented about include/ntuser.h:
+{ + UINT offset; + UINT size; +}; + +static inline LONG_PTR NtUserGetPrivateData( HWND hwnd, UINT offset, UINT size ) +{ + struct get_private_data_params params = { .offset = offset, .size = size }; + return NtUserCallHwndParam( hwnd, (UINT_PTR)¶ms, NtUserCallHwndParam_GetPrivateData ); +} + +struct set_private_data_params +{ + UINT offset; + UINT size; + LONG_PTR value; In this form, it would also require a change in `wow64_NtUserCallHwndParam`. You could also use LONG64 here.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10277#note_131622
participants (3)
-
Jacek Caban (@jacek) -
Piotr Caban -
Piotr Caban (@piotr)