Opened 15 years ago
Closed 15 years ago
#1362 closed Bug (Fixed)
_WinAPI_WindowFromPoint() broken on x64
Reported by: | Ultima | Owned by: | Valik |
---|---|---|---|
Milestone: | 3.3.3.0 | Component: | AutoIt |
Version: | 3.3.2.0 | Severity: | None |
Keywords: | WindowFromPoint x64 | Cc: |
Description
Indeed, I've seen #974, but I think the problem I'm addressing is different.
Currently, if you run _WinAPI_WindowFromPoint() under an x64 OS, you'll notice that the function always returns a handle to the desktop window. This can be verified by testing the example script for _WinAPI_WindowFromPoint() -- you'll find that the tooltip almost always shows the same handle.
Why is it happening? The problem seems to be in the way the parameters are passed to the user32.dll. WindowFromPoint actually expects a 64-bit POINT structure to be passed by value, but _WinAPI_WindowFromPoint() passes the two parameters. This in and of itself should be fine, as explained here, but I suspect (though don't know for a fact) on the x64 build of AutoIt, one of the 32-bit parameters used as components for the coordinates ends up getting padded into a 64-bit integer, and the other one ends up getting thrown out.
To work around this, I ended up recasting the passed $tPoint parameter from "long;long" into "int64", and passed the value from that casted struct into DllCall instead. From my basic tests, the result is that this properly fixes x64 support, while maintaining x86 support.
Here's the patched code:
Func _WinAPI_WindowFromPoint(ByRef $tPoint) Local $tPointCast = DllStructCreate("int64", DllStructGetPtr($tPoint)) Local $aResult = DllCall("user32.dll", "hwnd", "WindowFromPoint", "int64", DllStructGetData($tPointCast, 1)) If @error Then Return SetError(@error, @extended, 0) Return $aResult[0] EndFunc ;==>_WinAPI_WindowFromPoint
Attachments (0)
Change History (10)
comment:1 Changed 15 years ago by Ultima
comment:2 Changed 15 years ago by Valik
What happens if you reverse the X and Y points on x64? There's no reason at all for the behavior you describe. The size of a POINT structure on x64 is still 64-bits composed of two 32-bit signed integers. The only thing I can think of is if the calling convention is different on x64 (I can't see any sign that it is, however). This would cause the values to be flipped. So, try flipping them yourself (fill the X member with the Y data and the Y member with the X data).
comment:3 Changed 15 years ago by anonymous
To add a bit more information (in case this is relevant): I'm running Windows 7 Professional x64.
I tried your suggestion, but saw no relevant difference.
From a few further tests, it indeed seems like the Y member (the second value in the parameter passed to WindowFromPoint) is the one getting thrown out. How I tested this was to place a window at the very top edge of the screen (so that it covers the desktop even when the Y coordinate is 0). When I do that, I can see the handle shown in the example script's tooltip change exactly when I mouse over an X coordinate that the window covers, no matter what the mouse's actually Y coordinate is (that is, even if the mouse cursor is placed below the bottom edge of the window). The fixed (hacked) function reveals that the value of the handle I see is indeed the value of the window that I placed at the top edge of the screen.
(Note: When I use your suggestion with my test, the only difference is that the tooltip shows the different handle when the mouse's Y coordinate is equal to an X coordinate that the window is covering at the top of the screen -- basically, the screen coordinates are transposted, as expected)
This is pure conjecture, but I think the following quote from the KB article I linked to in the ticket description might be relevant (and perhaps, match your suspicions about calling conventions):
Another important consideration is that 32-bit Visual Basic uses the C convention (stdcall) of passing parameters. This convention specifies that arguments are placed on the stack from right to left. 16-Bit Visual Basic maintains the Pascal convention of passing parameters from left to right. (API functions are declared using the Pascal calling convention.) As a result, the elements of the structure must be listed in reverse order (that is, element y followed by x) when calling the WindowFromPoint function using 32-Bit Visual Basic. When using 16-bit Visual Basic, element x is passed to the API function before element y.
I understand the POINT structure as defined in StructureConstants.au3 ("long x;long y") would still be a 64-bit structure composed of two 32-bit long integers, but I'm not sure if that actually helps, since as I understand it (admittedly not completely), AutoIt can't pass structures by value. To expand on my previous point about "padding" to x64, this is how _WinAPI_WindowFromPoint() currently calls "WindowFromPoint" in user32:
Local $iX = DllStructGetData($tPoint, "X") Local $iY = DllStructGetData($tPoint, "Y") Local $aResult = DllCall("user32.dll", "hwnd", "WindowFromPoint", "long", $iX, "long", $iY)
Would this at all cause a 64-bit integer to be placed on the stack when it extracts the components from the DllStruct and passes the extracted value along to DllCall?
comment:4 Changed 15 years ago by Ultima
(Uh, sorry, failed to change my username above. That was indeed me, the original poster)
comment:5 Changed 15 years ago by Valik
I cannot imagine anything that can be causing the size to change short of a bug in the handling of the "long" type. I will review the code tomorrow. Failing to find anything there, would you be willing to test a simple custom DLL written by me which mimics the input for WindowFromPoint() and reports what it actually receives?
comment:6 Changed 15 years ago by Ultima
Sure thing :)
comment:7 Changed 15 years ago by Valik
I definitely see something in the code that could be causing this. I may not need help as I think I can reproduce the problem myself on 32-bit Windows using a custom DLL.
comment:8 Changed 15 years ago by Valik
After looking at how AutoIt works and doing some reading I've determined the following: parameters are always pushed onto the stack in the size of the architecture. On x86 every parameter <= 4 bytes takes up 4 bytes and on x64 every parameter <= 8 bytes takes up 8 bytes (even a char parameter). It should be obvious that if the structure is expected to be 8 bytes large with two distinct values aligned on 4-byte boundaries but x64 pads the values apart that things won't work - exactly as you determined.
Your fix seems correct. The data must be packed into an 8 byte (64-bit) integer in order to prevent stack-alignment issues and preserve the expected structure alignment.
comment:9 Changed 15 years ago by Ultima
And here I was, raring to go and test that DLL should the need have arisen ;P
Anyhow, good to see that it's settled then!
comment:10 Changed 15 years ago by Valik
- Milestone set to 3.3.3.0
- Owner set to Valik
- Resolution set to Fixed
- Status changed from new to closed
Fixed by revision [5496] in version: 3.3.3.0
Guidelines for posting comments:
- You cannot re-open a ticket but you may still leave a comment if you have additional information to add.
- In-depth discussions should take place on the forum.
For more information see the full version of the ticket guidelines here.
As an aside, I'm not sure how many other Windows API functions actually expect POINT structures to be passed by value, but if there are any other UDFs that rely on such functions, they may need to be fixed as well.