Use DllCall(..) to call GetKeyboardState

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
Archimede
Posts: 464
Joined: 25 Nov 2021, 09:49
Location: Switzerland / Italy

Use DllCall(..) to call GetKeyboardState

Post by Archimede » 10 Aug 2022, 09:52

Hallo.
I am tryng to call this API function:
BOOL GetKeyboardState( [out] PBYTE lpKeyState)
I tryed this:

Code: Select all

iTest := VarSetCapacity(sVarTest, 256, 64)
sTest := DllCall("GetKeyboardState", Ptr, &sVarTest)
sShiftLevel := NumGet(sVarTest, 15, "Char")                            ; 0x10 	SHIFT key
sShiftLeftLevel := NumGet(sVarTest, 159, "Char")                    ; 0xA0 	Left SHIFT key
sShiftRightLevel := NumGet(sVarTest, 160, "Char")                  ; 0xA1 	Right SHIFT key
but it seem no works well: can you tell me why?
It no gives the SHIFT key level: why?
The DllCall return value is 1, then it seem the call is right...

swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: Use DllCall(..) to call GetKeyboardState

Post by swagfag » 10 Aug 2022, 11:14

because 0x10 is not 15, 0xA0 is not 159 and 0xA1 is not 160

Archimede
Posts: 464
Joined: 25 Nov 2021, 09:49
Location: Switzerland / Italy

Re: Use DllCall(..) to call GetKeyboardState

Post by Archimede » 10 Aug 2022, 11:33

:-(
You have reason, but with the conversion Hex -> Dec
10 -> 16
A0 -> 160
A1 -> 161
that are the positions on the string; then the offsets are:
16 - 1 = 15
160 -1 = 159
161 -1 = 160
NumGet(...) need offset: where is the error?

swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: Use DllCall(..) to call GetKeyboardState

Post by swagfag » 10 Aug 2022, 12:08

the error is in ur preconceived notions about the layout of the array thats being populated by the function call. theyre incorrect, simple as

Archimede
Posts: 464
Joined: 25 Nov 2021, 09:49
Location: Switzerland / Italy

Re: Use DllCall(..) to call GetKeyboardState

Post by Archimede » 10 Aug 2022, 16:23

Interesting.
What is the solution?
I tried to no set the 256 byres string but I found the *same* result... :-(

swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: Use DllCall(..) to call GetKeyboardState

Post by swagfag » 10 Aug 2022, 16:34

the solution is to use the correct offsets. per the MSDN docs:
An application can use the virtual-key code constants VK_SHIFT, VK_CONTROL and VK_MENU [as well as any of the other remaining constants] as indices into the array pointed to by lpKeyState.
if its still not clear, the correct offsets are 0x10 (16), 0xA0 (160) and 0xA1 (161). those numbers u plug into NumGet()

Archimede
Posts: 464
Joined: 25 Nov 2021, 09:49
Location: Switzerland / Italy

Re: Use DllCall(..) to call GetKeyboardState

Post by Archimede » 10 Aug 2022, 16:48

Thank you.
Heare it is night, tomorrow I will try it.
But I no understand a thing:
You (and Microsoft) indicates what must be the pointer; NumGet(...) needs an offset, no a pointer, and starting from 0 the offset = pointer - 1... or not?

Archimede
Posts: 464
Joined: 25 Nov 2021, 09:49
Location: Switzerland / Italy

Re: Use DllCall(..) to call GetKeyboardState

Post by Archimede » 11 Aug 2022, 03:52

Hallo.
I used these pointer:
16
160
161
I tryed to change pointers, like you told me, but I have no obtained any good result, only result without meaning.
Can anyone tell me where is the error?
Thank you very much

swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: Use DllCall(..) to call GetKeyboardState

Post by swagfag » 11 Aug 2022, 06:42

no, i cant. i dont understand what "I have no obtained any good result, only result without meaning" means nor do i see what code u ran. for all i care, u could have written it wrong

Archimede
Posts: 464
Joined: 25 Nov 2021, 09:49
Location: Switzerland / Italy

Re: Use DllCall(..) to call GetKeyboardState

Post by Archimede » 11 Aug 2022, 15:34

Do you tested the code?

swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: Use DllCall(..) to call GetKeyboardState

Post by swagfag » 11 Aug 2022, 16:32

which code? the unedited one from ur OP, containing wrong offsets? no, i havent tested it. i dont see the point, since its clearly wrong... as already pointed out

qomph
Posts: 3
Joined: 11 Aug 2023, 22:48
Contact:

Re: Use DllCall(..) to call GetKeyboardState

Post by qomph » 11 Aug 2023, 23:11

This is part of a larger automation script I've been hacking away at for some time.

I found your post, and was inspired by it to try GetKeyboardState, but as you can see, I learned that it is reading the keyboard state of the AHK v2 process and not the process I was interested in. I think this was probably your problem.

I included the relevant code to update the title bar because I found this to be an interesting method of debugging keystate-related issues (like, why are some prefix keys stuck down sometimes? Although I haven't used it long enough to debug such issues much yet...)

Code: Select all


Debug_KeyState := true
;...
GetAllKeyState() {
	kss := ""
	freq := 0
	CounterBefore := 0
	CounterAfter := 0
	;Critical "On" ;; testing to see if the timing would be more stable
	DllCall("QueryPerformanceFrequency", "Int64*", &freq)
	DllCall("QueryPerformanceCounter", "Int64*", &CounterBefore)
	if true  {
		Loop 255 {
			vk := Format("vk{:X}", A_Index)
			ks := GetKeyState(vk, "P")
			if ks {
				n := GetKeyName(vk)
				kss .= n . "(" . vk . ") "
			}
		}
	} else {
		;; the problem with GetKeyboardState is that it reads only from the AHK process
		;;https://www.autohotkey.com/boards/viewtopic.php?style=19&t=107229&p=476837
		keystate := Buffer(256)
		gks := DllCall("GetKeyboardState", "Ptr", keystate, "Cdecl")
		if gks == 0 {
			kss .= "gks=error! "
		}
		Loop 255 {
			ks := NumGet(keystate,A_Index,"UChar")
			if ks >= 128 {
				vk := Format("vk{:X}", A_Index)
				n := GetKeyName(vk)
				kss .= Format("{}({})({}) ",n,vk,ks)
			}
		}
	}
	if StrLen(kss)>0 {
		DllCall("QueryPerformanceCounter", "Int64*", &CounterAfter)
		; kss .= "" . Format("{:d} µs", (CounterAfter - CounterBefore) * 1000000 / freq)
	}
	;Critical "Off"
	return kss
}

~^!Home::ClearAllKeyState()

ClearAllKeyState() {
	kss := ""
	WinSetTitle("Clearing key state", "ahk_exe GTA5.exe")
	Loop 255 {
		vk := Format("\{vk{:X} UP\}", A_Index)
		Send(vk)
	}
	return kss
}

UpdateTitleBar_KeyState() {
	global last_title := ""
	kss := ""
	kss := GetAllKeyState()
	current_title := GTAtitle "`: " kss
	if last_title != current_title {
		WinSetTitle(current_title, "ahk_exe GTA5.exe")
		last_title := current_title
	}
	;;Send("{vk9E UP}");;didnt work
}

if Debug_KeyState {
	SetTimer(UpdateTitleBar_KeyState,1000,0)
}
Return

qomph
Posts: 3
Joined: 11 Aug 2023, 22:48
Contact:

Re: Use DllCall(..) to call GetKeyboardState

Post by qomph » 16 Aug 2023, 07:34

This is part of a larger automation script I've been hacking away at for some time.

I found your post, and was inspired by it to try GetKeyboardState, but as you can see, I learned that it is reading the keyboard state of the AHK v2 process and not the process I was interested in. I think this was probably your problem.

I included the relevant code to update the title bar because I found this to be an interesting method of debugging keystate-related issues (like, why are some prefix keys stuck down sometimes? Although I haven't used it long enough to debug such issues much yet...)

Code: Select all


Debug_KeyState := true
;...
GetAllKeyState() {
	freq := 0
	CounterBefore := 0
	CounterAfter := 0
	;Critical "On" ;; testing to see if the timing would be more stable
	;~ DllCall("QueryPerformanceFrequency", "Int64*", &freq)
	;~ DllCall("QueryPerformanceCounter", "Int64*", &CounterBefore)
	;~ kss := ""
	kp := "" ; physical key states
	kl := "" ; logical key states
	kb := "" ; both
	Loop 255 {
		vk := Format("vk{:X}", A_Index)
		n := GetKeyName(vk)
		p := GetKeyState(vk, "P")
		l := GetKeyState(vk)
		;; display vk only if not [0-9A-Z]
		if A_Index >= 48 && A_Index <= 57 || A_Index >= 65 && A_Index <= 90 {
			ks := n . " "
		} else {
			ks := n . "(" . vk . ") "
		}
		if p && l && p == l {
			kb .= ks
		} else if l && !p {
			kl .= ks
		} else if p && !l {
			kp .= ks
		}
	}
	kss := ""
	if StrLen(kb) {
		kss .= " keyState:" . kb
	}
	if StrLen(kp) {
		kss .= "Phys:" . kp
	}
	if StrLen(kl) {
		kss .= " Logi:" . kl
	}
	;~ if StrLen(kss)>0 {
		;~ DllCall("QueryPerformanceCounter", "Int64*", &CounterAfter)
		;~ kss .= "" . Format("{:d} µs", (CounterAfter - CounterBefore) * 1000000 / freq)
	;~ }
	;Critical "Off"
	return kss
}

~^!Home::ClearAllKeyState()

ClearAllKeyState() {
	WinSetTitle("Clearing key state", "ahk_exe GTA5.exe")
	Loop 255 {
		vk := Format("\{vk{:X} UP\}", A_Index)
		Send(vk)
	}
}

global current_keystate := ""
global last_keystate  := ""

updateTitleBar() {
	global last_keystate
	if last_keystate == "suspended" {
		return
	}
	if last_keystate != current_keystate  {
		title := GTAtitle . "`: " . current_keystate
		WinSetTitle(title, "ahk_exe GTA5.exe")
		last_keystate := current_keystate
	}
}

UpdateTitleBar_KeyState() {
	global current_keystate := GetAllKeyState()
	updateTitleBar()
}

if Debug_KeyState {
	SetTimer(UpdateTitleBar_KeyState,1000,0)
}
Return

For more about keystate, see
https://github.com/tallpeak/AHK

and a recent stream on twitch.tv/sqlexpert

Post Reply

Return to “Ask for Help (v1)”