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: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)
Note: See TracTickets for help on using tickets.