Opened 21 months ago
Last modified 8 months ago
#3942 assigned Feature Request
@EVT_POS, @EVT_TIME, @EVT_INFO event macros — at Version 3
Reported by: | AutoXenon | Owned by: | Jon |
---|---|---|---|
Milestone: | Component: | AutoIt | |
Version: | Severity: | None | |
Keywords: | Cc: |
Description (last modified by mLipok)
References:
learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getmessagepos learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getmessagetime learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getmessageextrainfo devblogs.microsoft.com/oldnewthing/20121101-00/?p=6193
Motivation:
Manually calling MouseGetPos() when an OnEvent function is triggered may result in an incorrect position as there might be new mouse movements from the time the message is posted. The correct way to retrieve the associated cursor position or message timestamp is to make a DllCall to GetMessagePos/GetMessageTime within the OnEvent function. This results in frequently re-used boilerplate code in every GUISetOnEvent and GUIRegisterMsg callbacks. It also has a bit of overhead for frequently received messages, having to call it from within AutoIt's interpreter
Since these data are always associated with every Windows Message, it makes sense to just make it a built-in macro that is called from the C++ core for all Windows Messages. The syscall is pretty fast so I don't think it would be necessary to make it optionally activated, but if it turns out that it needs it after all then we could always just have it hidden behind an Opt('EventExtraInfo',1) flag or something like that.
Note that the raw @EVT_POS returned from the WinAPI call is a 32-bit dword where x and y are signed 16-bits. In my boilerplates I use DllStructCreate with pointers to recast them into two signed shorts like so:
Func GetMessagePos($dll='user32.dll') Local $pos = DllCall($dll,'dword','GetMessagePos')[0] Local $_ = DllStructCreate('dword;') DllStructSetData($_,1,$pos) Local $val = DllStructCreate('short x;short y',DllStructGetPtr($_)) Local $pos = [$val.x,$val.y] Return $pos EndFunc
And the boilerplates for GetMessageTime and GetMessageExtraInfo is as follows
Func GetMessageTime($dll='user32.dll') Return DllCall($dll,'long','GetMessageTime')[0] EndFunc Func GetMessageExtraInfo($dll='user32.dll') Return DllCall($dll,'lparam','GetMessageExtraInfo')[0] EndFunc
A sample application using the above boilerplates:
#OnAutoItStartRegister SetProcessDPIAware Opt("GUIOnEventMode",1) Global Const $user32 = DllOpen('user32.dll') GUICreate('',@DesktopWidth,@DesktopHeight,0,0,0x80000000,8) GUISetOnEvent(-3,Quit) GUISetOnEvent(-7,OnPrimaryDown) GUISetOnEvent(-8,OnPrimaryUp) GUISetOnEvent(-9,OnSecondaryDown) GUISetOnEvent(-10,OnSecondaryUp) GUISetState() HotKeySet('{space}',OnSpace) While Sleep(1000) WEnd Func Quit() Exit EndFunc Func OnPrimaryDown() Local $pos = GetMessagePos($user32), $time = GetMessageTime($user32), $info = GetMessageExtraInfo($user32) ToolTip('pos: '&$pos[0]&','&$pos[1]&@CRLF&'time: '&$time&@CRLF&'info: '&Hex($info),$pos[0],$pos[1],'PrimaryDown') EndFunc Func OnSecondaryDown() Local $pos = GetMessagePos($user32), $time = GetMessageTime($user32), $info = GetMessageExtraInfo($user32) ToolTip('pos: '&$pos[0]&','&$pos[1]&@CRLF&'time: '&$time&@CRLF&'info: '&Hex($info),$pos[0],$pos[1],'SecondaryDown') EndFunc Func OnPrimaryUp() ToolTip('') EndFunc Func OnSecondaryUp() ToolTip('') EndFunc Func OnSpace() Local Static $on = False $on = not $on Local $_ = DllStructCreate('dword;struct;long;long;dword;dword;dword;ulong_ptr;endstruct;') DllStructSetData($_,1,0) DllStructSetData($_,5,$on ? 2 : 4) DllStructSetData($_,6,420) DllStructSetData($_,7,0xdeadbeef) Return DllCall( $user32, 'uint', 'SendInput', 'uint', 1, 'struct*', $_, 'int', DllStructGetSize($_) )[0] EndFunc Func GetMessagePos($dll='user32.dll') Local $pos = DllCall($dll,'dword','GetMessagePos')[0] Local $_ = DllStructCreate('dword;') DllStructSetData($_,1,$pos) Local $val = DllStructCreate('short;short',DllStructGetPtr($_)) Local $pos = [DllStructGetData($val,1),DllStructGetData($val,2)] Return $pos EndFunc Func GetMessageTime($dll='user32.dll') Return DllCall($dll,'long','GetMessageTime')[0] EndFunc Func GetMessageExtraInfo($dll='user32.dll') Return DllCall($dll,'lparam','GetMessageExtraInfo')[0] EndFunc Func SetProcessDPIAware() GUICreate('') DllCall('user32.dll', 'bool', 'SetProcessDPIAware') GUIDelete() EndFunc
Change History (3)
comment:1 Changed 21 months ago by anonymous
comment:2 Changed 8 months ago by Jpm
- Owner set to Jon
- Status changed from new to assigned
I change a little bit the naming to
@GUIEVT_CURSORPOSX, @GUIEVT_CURSORPOSY, @GUIEVT_ELAPSETIME, @GUIEVT_EXTRAINFO.
as the time is relative to Windows Start I prefer to return and elapsetime relative to the Gui visible time (GUISetState() time)
and also for coherency rename @GUIEVT_WINHANDLE, @GUIEVT_CTRHANDLE, @GUIEVT_CTRLID.
@GUI_* still compatible.
I sent the Fix to Jon
comment:3 Changed 8 months ago by mLipok
- Description modified (diff)
More ref: https://devblogs.microsoft.com/oldnewthing/20140122-00/?p=2013