Page 1 of 1

AutoHotkey source code: Send/ControlSend (modifier keys)

Posted: 07 Jan 2017, 11:50
by jeeswg
I have been able to understand the AutoHotkey source code for most commands/functions when needed, however the Send/ControlSend source code seems to be rather complicated.
If anyone understands it well, would they please give me or point me to a summary of some of the main issues/techniques.

My main questions are:
- How does AutoHotkey do shift/ctrl/win/alt + letter?
- How can an AutoHotkey GUI detect shift/ctrl/win/alt + letter, i.e. what should it check for in the message queue.

One problem I've had with Send is that if the active window changes, it sends key presses to the wrong window.
So I created 'secure' versions:

Code: Select all

;send secure (where hWnd is a window/control hWnd):
ControlSend,, % vOutput, % "ahk_id " hWnd

;send raw secure (where hWnd is a window/control hWnd):
Loop Parse, vOutput
	PostMessage, 0x102, % Ord(A_LoopField), 1,, % "ahk_id " hWnd ;WM_CHAR := 0x102
Another problem I had is, for example, the euro character would not send properly.
I believe AutoHotkey tries to send (or used to in the past) ctrl+alt+4.
However, WM_CHAR, as used above, seems to work consistently.
Another example is é and ctrl+alt+e.

Re: AutoHotkey source code: Send/ControlSend (modifier keys)

Posted: 12 Jan 2017, 14:33
by jeeswg
For example, can anyone tell me how to send Ctrl+Shift+Left/Ctrl+Shift+Right via SendMessage (or Acc (MSAA)/UI Automation if that's possible)?

Re: AutoHotkey source code: Send/ControlSend (modifier keys)  Topic is solved

Posted: 30 Jan 2017, 14:24
by jeeswg
Alternatives to Send/SendInput/ControlSend:
To send text (individual Unicode characters): WM_CHAR.
To send an individual key press: WM_KEYDOWN, WM_KEYUP.
To send an individual key press plus modifiers:
WM_KEYDOWN, WM_KEYUP, but also use SetKeyboardState as in the example below.

To receive an individual key press, and to work out the correct parameter values for WM_KEYDOWN, WM_KEYUP, that you would use to send a key press:
Use OnMessage to check for the 2 messages, and receive the parameters, for when keys are pressed manually or sent via Send/SendInput.

To receive an individual key press, and modifiers:
Use OnMessage to check for the key press and use GetKeyState to check for modifiers.
Instead of GetKeyState, the dll function GetKeyboardState can be used.

The reasons for investigating Send/SendInput, were to understand how best to receive input when creating GUIs via AutoHotkey, and to be able to send key presses to a specific hWnd unlike via Send/SendInput.
Further reasons are to understand how key presses can be sent in programming languages generally, without standard AutoHotkey functions, and to understand the AutoHotkey source code.
To find out how AutoHotkey does this, search the cpp source code files for 'GetKeyboardState' and 'SetKeyboardState'.

Note: regarding methods outlined here, there may be other methods.
Note: the code below worked although it is possible that improvements could be made.

If anyone has any further comments regarding these issues, or the code, or regarding AutoHotkey and receiving/sending key presses, they would be most welcome.

Code: Select all

q:: ;send ctrl+shift+left without using Send/SendInput/ControlSend
;e.g. tested on Notepad (Windows 7)
WinGet, hWnd, ID, A
ControlGetFocus, vCtlClassNN, % "ahk_id " hWnd
if !(vCtlClassNN = "")
	ControlGet, hWnd, Hwnd,, % vCtlClassNN, % "ahk_id " hWnd

vTIDAhk := DllCall("kernel32\GetCurrentThreadId", "UInt")
VarSetCapacity(PBYTE1, 256, 0)
DllCall("user32\GetKeyboardState", "Ptr",&PBYTE1)

vTID := DllCall("user32\GetWindowThreadProcessId", "Ptr",hWnd, "Ptr",0, "UInt")
DllCall("user32\AttachThreadInput", "UInt",vTIDAhk, "UInt",vTID, "Int",1)
VarSetCapacity(PBYTE2, 256, 0)
NumPut(0x80, PBYTE2, 0x10, "UChar") ;VK_SHIFT := 0x10
NumPut(0x80, PBYTE2, 0x11, "UChar") ;VK_CONTROL := 0x11
DllCall("user32\SetKeyboardState", "Ptr",&PBYTE2)

;VK_LEFT := 0x25
SendMessage, 0x100, 0x25, 0x014B0001,, % "ahk_id " hWnd ;WM_KEYDOWN := 0x100
SendMessage, 0x101, 0x25, 0xC14B0001,, % "ahk_id " hWnd ;WM_KEYUP := 0x101
DllCall("user32\AttachThreadInput", "UInt",vTIDAhk, "UInt",vTID, "Int",0)
DllCall("user32\SetKeyboardState", "Ptr",&PBYTE1)
return

Re: AutoHotkey source code: Send/ControlSend (modifier keys)

Posted: 30 Jan 2017, 14:40
by jeeswg
Useful links:

[ list of VK constants]
Virtual-Key Codes (Windows)
https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx

[useful code]
method to detect key combinations - Scripts and Functions - AutoHotkey Community
https://autohotkey.com/board/topic/6978-method-to-detect-key-combinations/

[useful approach comments]
Need some advice for sending CTRL in global mousehook - Ask for Help - AutoHotkey Community
https://autohotkey.com/board/topic/3051-need-some-advice-for-sending-ctrl-in-global-mousehook/

GetKeyboardState to save old state, AttachThreadInput, SetKeyboardState for SHIFT or CTRL, Send/PostMessage, Sleep (needed so foreign application can process message), AttachThreadInput (to detach) and SetKeyboardState to restore old state.

[EDIT:] Similar question to this thread (topic):
PostMessage plz help - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=30925

Re: AutoHotkey source code: Send/ControlSend (modifier keys)

Posted: 31 Jan 2017, 03:22
by jeeswg
Raw messages for key presses:
Btw these are the messages received when sending keys manually or using SendInput to achieve ctrl+shift+left, (the 6 messages were the same in both cases,) but sending them via SendMessage only resulted in send left.

Note: SendInput appeared to work consistently to send ctrl+shift+left, but ControlSend only appeared to work intermittently.

Code: Select all

q:: ;did not work to send ctrl+shift+left, only sent left
WinGet, hWnd, ID, A
ControlGetFocus, vCtlClassNN, % "ahk_id " hWnd
if !(vCtlClassNN = "")
	ControlGet, hWnd, Hwnd,, % vCtlClassNN, % "ahk_id " hWnd

SendMessage, 0x100, 0x11, 0x001D0001,, % "ahk_id " hWnd ;WM_KEYDOWN := 0x100
SendMessage, 0x100, 0x10, 0x002A0001,, % "ahk_id " hWnd ;WM_KEYDOWN := 0x100
SendMessage, 0x100, 0x25, 0x014B0001,, % "ahk_id " hWnd ;WM_KEYDOWN := 0x100
Sleep, 200
SendMessage, 0x101, 0x25, 0xC14B0001,, % "ahk_id " hWnd ;WM_KEYUP := 0x101
SendMessage, 0x101, 0x11, 0xC01D0001,, % "ahk_id " hWnd ;WM_KEYUP := 0x101
SendMessage, 0x101, 0x10, 0xC02A0001,, % "ahk_id " hWnd ;WM_KEYUP := 0x101
return

;==================================================

;for reference (reliable):
;q::
SendInput, ^+{Left}
return

;for reference (unreliable, worked but intermittently):
;q::
ControlSend,, ^+{Left}, A
return

;for reference (unreliable, worked but intermittently):
;q::
ControlSend, Edit1, ^+{Left}, A
return

;for reference (unreliable, worked but intermittently):
;q::
WinGet, hWnd, ID, A
ControlGetFocus, vCtlClassNN, % "ahk_id " hWnd
if !(vCtlClassNN = "")
	ControlGet, hWnd, Hwnd,, % vCtlClassNN, % "ahk_id " hWnd
ControlSend,, ^+{Left}, % "ahk_id " hWnd
return
[EDIT:] A related thread:
SendInput specify hWnd / improved ControlSend - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=37120

Re: AutoHotkey source code: Send/ControlSend (modifier keys)

Posted: 09 May 2020, 07:21
by dxahk
Question: What do I need to alter this to send, say, 0x20 (a space)? I've changed 0x25 to 0x20, but do I need to change the lParam as well? I don't know how to determine the correct value to use for it.