[PATCH v3 0/1] MR11373: Improve wow64_NtUserGetPointerInfoList.
Hello, I noticed a failure in win32u:win32u, probably related to !11332. I tried to take a look and it seems a new-wow64 only issue. I came up with this patches transferring the 64-bit structs into 32-bit one. Are they going into the right direction? These patches fix the failure in win32.c:1974, but they introduce now a new wow64-only failure with win32.c:1935, the last invalid_ptr test. I can see at the 64-bit side we hit the SIGSEGV, but somehow at the 32-bit side it still returns without failure. Somehow I fail to find any other similar wow64 function that has special handling of invalid pointers, what I am missing? [Test pattern page](https://test.winehq.org/data/patterns.html#win32u:win32u) CC: @rbernon -- v3: wow64win: Improve wow64_NtUserGetPointerInfoList. https://gitlab.winehq.org/wine/wine/-/merge_requests/11373
From: Bernhard Übelacker <bernhardu@mailbox.org> --- dlls/user32/tests/input.c | 1 + dlls/win32u/tests/win32u.c | 1 + dlls/wow64win/user.c | 107 +++++++++++++++++++++++++++++++++++++ 3 files changed, 109 insertions(+) diff --git a/dlls/user32/tests/input.c b/dlls/user32/tests/input.c index 382703452d3..e1779f5c156 100644 --- a/dlls/user32/tests/input.c +++ b/dlls/user32/tests/input.c @@ -5771,6 +5771,7 @@ static void test_GetPointerInfo( BOOL mouse_in_pointer_enabled ) ok( class, "RegisterClassW failed: %lu\n", GetLastError() ); ret = pGetPointerInfo( 1, invalid_ptr ); + todo_wine_if(ret == STATUS_ACCESS_VIOLATION) ok( !ret, "GetPointerInfo succeeded\n" ); todo_wine ok( GetLastError() == ERROR_NOACCESS || broken(GetLastError() == ERROR_INVALID_PARAMETER) /* w10 32bit */, diff --git a/dlls/win32u/tests/win32u.c b/dlls/win32u/tests/win32u.c index f13c4f34870..fd0ca631a89 100644 --- a/dlls/win32u/tests/win32u.c +++ b/dlls/win32u/tests/win32u.c @@ -1933,6 +1933,7 @@ static void test_NtUserGetPointerInfoList( BOOL mouse_in_pointer_enabled ) ok( GetLastError() == ERROR_NOACCESS, "got error %lu\n", GetLastError() ); entry_count = pointer_count = 2; ret = NtUserGetPointerInfoList( 1, PT_POINTER, 0, 0, sizeof(POINTER_INFO), &entry_count, &pointer_count, invalid_ptr ); + todo_wine_if(ret == STATUS_ACCESS_VIOLATION) ok( !ret, "NtUserGetPointerInfoList succeeded\n" ); todo_wine ok( GetLastError() == ERROR_NOACCESS || broken(GetLastError() == ERROR_INVALID_PARAMETER) /* w10 32bit */, "got error %lu\n", GetLastError() ); diff --git a/dlls/wow64win/user.c b/dlls/wow64win/user.c index 5506c9fc5b0..c636903a6dc 100644 --- a/dlls/wow64win/user.c +++ b/dlls/wow64win/user.c @@ -338,6 +338,48 @@ typedef struct ULONG hIconSm; } WNDCLASSEXW32; +typedef struct +{ + DWORD pointerType; + UINT32 pointerId; + UINT32 frameId; + UINT32 pointerFlags; + ULONG sourceDevice; + ULONG hwndTarget; + POINT ptPixelLocation; + POINT ptHimetricLocation; + POINT ptPixelLocationRaw; + POINT ptHimetricLocationRaw; + DWORD dwTime; + UINT32 historyCount; + INT32 InputData; + DWORD dwKeyStates; + UINT64 PerformanceCount; + INT32 ButtonChangeType; +} POINTER_INFO32; + +typedef struct +{ + POINTER_INFO32 pointerInfo; + PEN_FLAGS penFlags; + PEN_MASK penMask; + UINT32 pressure; + UINT32 rotation; + INT32 tiltX; + INT32 tiltY; +} POINTER_PEN_INFO32; + +typedef struct +{ + POINTER_INFO32 pointerInfo; + TOUCH_FLAGS touchFlags; + TOUCH_MASK touchMask; + RECT rcContact; + RECT rcContactRaw; + UINT32 orientation; + UINT32 pressure; +} POINTER_TOUCH_INFO32; + struct win_proc_params32 { ULONG func; @@ -2820,8 +2862,51 @@ NTSTATUS WINAPI wow64_NtUserGetOpenClipboardWindow( UINT *args ) return HandleToUlong( NtUserGetOpenClipboardWindow() ); } +static void pointer_info_32to64( POINTER_INFO32 *out, const POINTER_INFO *in ) +{ + out->pointerType = in->pointerType; + out->pointerId = in->pointerId; + out->frameId = in->frameId; + out->pointerFlags = in->pointerFlags; + out->sourceDevice = HandleToUlong(in->sourceDevice); + out->hwndTarget = HandleToUlong(in->hwndTarget); + out->ptPixelLocation = in->ptPixelLocation; + out->ptHimetricLocation = in->ptHimetricLocation; + out->ptPixelLocationRaw = in->ptPixelLocationRaw; + out->ptHimetricLocationRaw = in->ptHimetricLocationRaw; + out->dwTime = in->dwTime; + out->historyCount = in->historyCount; + out->InputData = in->InputData; + out->dwKeyStates = in->dwKeyStates; + out->PerformanceCount = in->PerformanceCount; + out->ButtonChangeType = in->ButtonChangeType; +} + +static void pointer_pen_info_32to64( POINTER_PEN_INFO32 *out, const POINTER_PEN_INFO *in ) +{ + pointer_info_32to64( &out->pointerInfo, &in->pointerInfo ); + out->penFlags = in->penFlags; + out->penMask = in->penMask; + out->pressure = in->pressure; + out->rotation = in->rotation; + out->tiltX = in->tiltX; + out->tiltY = in->tiltY; +} + +static void pointer_touch_info_32to64( POINTER_TOUCH_INFO32 *out, const POINTER_TOUCH_INFO *in ) +{ + pointer_info_32to64( &out->pointerInfo, &in->pointerInfo ); + out->touchFlags = in->touchFlags; + out->touchMask = in->touchMask; + out->rcContact = in->rcContact; + out->rcContactRaw = in->rcContactRaw; + out->orientation = in->orientation; + out->pressure = in->pressure; +} + NTSTATUS WINAPI wow64_NtUserGetPointerInfoList( UINT *args ) { + NTSTATUS ret; UINT id = get_ulong( &args ); UINT type = get_ulong( &args ); UINT unk0 = get_ulong( &args ); @@ -2831,6 +2916,28 @@ NTSTATUS WINAPI wow64_NtUserGetPointerInfoList( UINT *args ) void *pointer_count = get_ptr( &args ); void *pointer_info = get_ptr( &args ); + if (type == PT_POINTER && size == sizeof(POINTER_INFO32)) + { + POINTER_INFO pointer_info64; + ret = NtUserGetPointerInfoList( id, type, unk0, unk1, sizeof(pointer_info64), entry_count, pointer_count, &pointer_info64 ); + pointer_info_32to64( pointer_info, &pointer_info64 ); + return ret; + } + else if (type == PT_PEN && size == sizeof(POINTER_PEN_INFO32)) + { + POINTER_PEN_INFO pointer_pen_info64; + ret = NtUserGetPointerInfoList( id, type, unk0, unk1, sizeof(pointer_pen_info64), entry_count, pointer_count, &pointer_pen_info64 ); + pointer_pen_info_32to64( pointer_info, &pointer_pen_info64 ); + return ret; + } + else if (type == PT_TOUCH && size == sizeof(POINTER_TOUCH_INFO32)) + { + POINTER_TOUCH_INFO pointer_touch_info64; + ret = NtUserGetPointerInfoList( id, type, unk0, unk1, sizeof(pointer_touch_info64), entry_count, pointer_count, &pointer_touch_info64 ); + pointer_touch_info_32to64( pointer_info, &pointer_touch_info64 ); + return ret; + } + return NtUserGetPointerInfoList( id, type, unk0, unk1, size, entry_count, pointer_count, pointer_info ); } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11373
v3: - Write directly to out memory. TODO: Handle entry_count and pointer_count greater than 1. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/11373#note_147529
On Tue Jul 28 22:18:25 2026 +0000, Bernhard Übelacker wrote:
Thanks for taking a look, I was assuming only one element is (currently) needed, because NtUserGetPointerInfoList does currently set entry_count and pointer_count to 1. Currently `entry_count` and `pointer_count` are [only out parameter](https://gitlab.winehq.org/wine/wine/-/blob/master/dlls/win32u/input.c?ref_ty...) of `NtUserGetPointerInfoList`.
Also the tests set these variables to `2`, while only providing a size for one record, and the test succeeds, so it looks like both values are only set but not used in the function. Even providing memory for two records seems to make the function fail, like in this [testbot run](https://testbot.winehq.org/JobDetails.pl?Key=163912). In Wine's `NtUserGetPointerInfoList` is also [any size different than one record rejected](https://gitlab.winehq.org/wine/wine/-/blob/master/dlls/win32u/input.c?ref_ty...) (except for `PT_MOUSE`). So I am not sure how to proceed, do you have more details on how to retrieve multiple records in a single call from `NtUserGetPointerInfoList`? Do we need to handle this in the wow64 layer even when the implementation does not yet support it? -- https://gitlab.winehq.org/wine/wine/-/merge_requests/11373#note_147898
On Tue Aug 4 16:19:42 2026 +0000, Bernhard Übelacker wrote:
Currently `entry_count` and `pointer_count` are [only out parameter](https://gitlab.winehq.org/wine/wine/-/blob/master/dlls/win32u/input.c?ref_ty...) of `NtUserGetPointerInfoList`. Also the tests set these variables to `2`, while only providing a size for one record, and the test succeeds, so it looks like both values are only set but not used in the function. Even providing memory for two records seems to make the function fail, like in this [testbot run](https://testbot.winehq.org/JobDetails.pl?Key=163912). In Wine's `NtUserGetPointerInfoList` is also [any size different than one record rejected](https://gitlab.winehq.org/wine/wine/-/blob/master/dlls/win32u/input.c?ref_ty...) (except for `PT_MOUSE`). So I am not sure how to proceed, do you have more details on how to retrieve multiple records in a single call from `NtUserGetPointerInfoList`? Do we need to handle this in the wow64 layer even when the implementation does not yet support it? The `size` value is supposed to be the size of a single record, so that `NtUserGetPointerInfoList` can check that it's consistent with the pointer type. The buffer capacity is likely `*entry_count * *pointer_count` on input, as is described in `GetPointerFrameInfoHistory` documentation. The actual counts are returned on output, as described too, and that can be larger than capacity.
I don't know how to generate such case, and the tests only managed to create pointer frame one at a time, but I think `NtUserGetPointerInfoList` wow64 thunk should be ready for more, as it's backing `GetPointerFrameInfoHistory` and similar, so we don't trip on it later on. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/11373#note_148132
participants (3)
-
Bernhard Übelacker -
Bernhard Übelacker (@bernhardu) -
Rémi Bernon (@rbernon)