Page 1 of 1
Use DllCall(..) to call GetKeyboardState
Posted: 10 Aug 2022, 09:52
by Archimede
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...
Re: Use DllCall(..) to call GetKeyboardState
Posted: 10 Aug 2022, 11:14
by swagfag
because 0x10 is not 15, 0xA0 is not 159 and 0xA1 is not 160
Re: Use DllCall(..) to call GetKeyboardState
Posted: 10 Aug 2022, 11:33
by Archimede
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?
Re: Use DllCall(..) to call GetKeyboardState
Posted: 10 Aug 2022, 12:08
by swagfag
the error is in ur preconceived notions about the layout of the array thats being populated by the function call. theyre incorrect, simple as
Re: Use DllCall(..) to call GetKeyboardState
Posted: 10 Aug 2022, 16:23
by Archimede
Interesting.
What is the solution?
I tried to no set the 256 byres string but I found the *same* result...
Re: Use DllCall(..) to call GetKeyboardState
Posted: 10 Aug 2022, 16:34
by swagfag
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()
Re: Use DllCall(..) to call GetKeyboardState
Posted: 10 Aug 2022, 16:48
by Archimede
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?
Re: Use DllCall(..) to call GetKeyboardState
Posted: 11 Aug 2022, 03:52
by Archimede
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
Re: Use DllCall(..) to call GetKeyboardState
Posted: 11 Aug 2022, 06:42
by swagfag
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
Re: Use DllCall(..) to call GetKeyboardState
Posted: 11 Aug 2022, 15:34
by Archimede
Do you tested the code?
Re: Use DllCall(..) to call GetKeyboardState
Posted: 11 Aug 2022, 16:32
by swagfag
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
Re: Use DllCall(..) to call GetKeyboardState
Posted: 11 Aug 2023, 23:11
by qomph
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
Re: Use DllCall(..) to call GetKeyboardState
Posted: 16 Aug 2023, 07:34
by qomph
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