Page 1 of 1

How to simulate keystroke using SendInput via dllcall?

Posted: 29 Jul 2021, 15:24
by killmatt01
Since SendInput {Key Down} and SendInput {Key Up} is a bit slow in some cases (even if i used SetBatchLines, -1 and SetKeyDelay, -1, -1), I am now looking for a method which can do the same job faster (ahk SendInput could use up to 40ms on my laptop).
I found one way to do so is using

Code: Select all

DllCall("keybd_event", "Int", VirtualKey, "Int", ScanCode, "Int", 1, "Int", 0)
DllCall("keybd_event", "Int", VirtualKey, "Int", ScanCode, "Int", 2, "Int", 0)
Since that function(keybd_event) "has been superseded", I am now trying to use SendInput function via dllcall

I tried this:

Code: Select all

~*1::
    press_key("Space")
Return
press_key(key_name)
{
    VarSetCapacity(Key_Down, 40)
    VarSetCapacity(Key_Up, 40)
    static INPUT_KEYBOARD := 1, KEYEVENTF_EXTENDEDKEY := 0x0001, KEYEVENTF_KEYUP := 0x0002

    VirtualKey := GetKeyVK(key_name)
    ScanCode := GetKeySC(key_name)
    NumPut(INPUT_KEYBOARD, Key_Down, "UInt")
    NumPut(VirtualKey, Key_Down, 2*A_PtrSize, "UShort")
    NumPut(ScanCode, Key_Down, 2*A_PtrSize, "UShort")

    NumPut(INPUT_KEYBOARD, Key_Up, "UInt")
    NumPut(VirtualKey, Key_Up, 2*A_PtrSize, "UShort")
    NumPut(ScanCode, Key_Up, 2*A_PtrSize, "UShort")

    Suspend, On

    NumPut(KEYEVENTF_EXTENDEDKEY, Key_Down, A_PtrSize, "UInt")
    DllCall("SendInput", "UInt", 1, "Ptr", &Key_Down, "Int", 40)
    sleep, 100
    VarSetCapacity(Key_Down, 0)

    NumPut(KEYEVENTF_KEYUP, Key_Up, A_PtrSize, "UInt")
    DllCall("SendInput", "UInt", 1, "Ptr", &Key_Down, "Int", 40)

    Suspend, Off
    sleep, 100
    VarSetCapacity(Key_Up, 0)
}
However that doesn't work. There are problems in my function but i don't know where they are. Could someone help me on fixing my function? Thanks so much!

Re: How to simulate keystroke using SendInput via dllcall?

Posted: 04 Aug 2021, 20:06
by killmatt01

Code: Select all

singleSpace()
{
    VarSetCapacity(Key1_Down, A_PtrSize*5+4, 0)
    static INPUT_KEYBOARD := 1, KEYEVENTF_EXTENDEDKEY := 0x0001, KEYEVENTF_KEYUP := 0x0002
    VirtualKey := int2hex(GetKeyVK("Space"))
    ScanCode := int2hex(GetKeySC("Space"))
    NumPut(INPUT_KEYBOARD, Key1_Down, "UInt") ;4 bit
    NumPut(VirtualKey, Key1_Down, A_PtrSize, "UShort")
    NumPut(ScanCode, Key1_Down, A_PtrSize*2, "UShort")
    NumPut(KEYEVENTF_EXTENDEDKEY, Key1_Down, A_PtrSize*3, "UInt")
    able := DllCall("SendInput", "UInt", 1, "Ptr", &Key1_Down, "Int", A_PtrSize*5+4)
    Tooltip, % able
}
/*
DWORD type;              -> 0
  union {
         MOUSEINPUT mi; 
         KEYBDINPUT ki;    -> 4
         HARDWAREINPUT hi;
        };
  WORD wVk;                -> 8
  WORD wScan;              -> 16
  DWORD dwFlags;           -> 24
  DWORD time;              -> 28
  ULONG_PTR dwExtraInfo;   -> 36
*/
;convertions, collected from forum
int2hex(int)
{
    HEX_INT := 2, h := ""
    while (HEX_INT--)
    {
        n := (int >> (HEX_INT * 4)) & 0xf
        h .= n > 9 ? chr(0x37 + n) : n
        if (HEX_INT == 0 && HEX_INT//2 == 0)
            h .= ""
    }
    return "0x" h
}
Which will show a "0" in the tooltip. Any ideas??????
ps:
If the function returns zero, the input was already blocked by another thread.

Re: How to simulate keystroke using SendInput via dllcall?

Posted: 05 Aug 2021, 01:52
by swagfag
those struct offsets look super wrong. two adjacent words in the same struct padded out to a pointer is a dead giveaway something's not right. ure filling the struct with garbage uve put in random places, so the DllCall returning 0 is probably not indicative of "the input being already blocked by another thread" but is a generic error. ud know if u checked A_LastError(like the docs said u should)

anyhow, dllcalling SendInput has been done to death, so search the forums and either find the right offsets or copy someone else's correct implementation

also these hex conversions are pointless

Re: How to simulate keystroke using SendInput via dllcall?

Posted: 05 Aug 2021, 09:04
by killmatt01
swagfag wrote:
05 Aug 2021, 01:52
those struct offsets look super wrong. two adjacent words in the same struct padded out to a pointer is a dead giveaway something's not right. ure filling the struct with garbage uve put in random places, so the DllCall returning 0 is probably not indicative of "the input being already blocked by another thread" but is a generic error. ud know if u checked A_LastError(like the docs said u should)

anyhow, dllcalling SendInput has been done to death, so search the forums and either find the right offsets or copy someone else's correct implementation

also these hex conversions are pointless
Thanks for answering. I edited the offsets a bit. Well that worked for mouse input

Code: Select all

MouseDown(key_name := "LButton")
{
    If !Instr(key_name, "Button")
        Return False
    StructSize := A_PtrSize + 4*4 + A_PtrSize*2
    WhichDown := Instr(key_name, "L") ? 0x0002 : 0x0008
    ;MOUSEEVENTF_LEFTDOWN := 0x0002, MOUSEEVENTF_RIGHTDOWN := 0x0008
    VarSetCapacity(Key_Down, StructSize)
    NumPut(0, Key_Down, "UInt") ;4 bit
    NumPut(0, Key_Down, A_PtrSize, "UInt")
    NumPut(0, Key_Down, A_PtrSize + 4, "UInt")
    NumPut(WhichDown, Key_Down, A_PtrSize + 4*3, "UInt")
    DllCall("SendInput", "UInt", 1, "Ptr", &Key_Down, "Int", StructSize)
    VarSetCapacity(Key_Down, 0)
}
so i took a try on keyboard input. Plus I cannot find the right offset for this in this forum.

Re: How to simulate keystroke using SendInput via dllcall?  Topic is solved

Posted: 05 Aug 2021, 10:01
by teadrinker
An example:

Code: Select all

KeyArray := [{sc: 0x23, event: "Down"}, {sc: 0x23, event: "Up"}, {sc: 0x17, event: "Down"}, {sc: 0x17, event: "Up"}]

InputKeyboardEvents(KeyArray)

InputKeyboardEvents(KeyArray)
{
   static INPUT_KEYBOARD := 1, KEYEVENTF_KEYUP := 2, KEYEVENTF_SCANCODE := 8, InputSize := 16 + A_PtrSize*3
   VarSetCapacity(INPUTS, InputSize * KeyArray.MaxIndex(), 0)
   addr := &INPUTS
   for k, v in KeyArray
      addr := NumPut( (v.event = "Up" ? KEYEVENTF_KEYUP : 0) | KEYEVENTF_SCANCODE | (v.sc >> 8)
            , NumPut(v.sc & 0xFF
            , NumPut(INPUT_KEYBOARD, addr + 0) + 2, "UShort"), "UInt" ) + 8 + A_PtrSize*2

   DllCall("SendInput", "UInt", KeyArray.MaxIndex(), "Ptr", &INPUTS, "Int", InputSize)
}

Re: How to simulate keystroke using SendInput via dllcall?

Posted: 10 Aug 2021, 17:20
by killmatt01
teadrinker wrote:
05 Aug 2021, 10:01
An example:

Code: Select all

KeyArray := [{sc: 0x23, event: "Down"}, {sc: 0x23, event: "Up"}, {sc: 0x17, event: "Down"}, {sc: 0x17, event: "Up"}]

InputKeyboardEvents(KeyArray)

InputKeyboardEvents(KeyArray)
{
   static INPUT_KEYBOARD := 1, KEYEVENTF_KEYUP := 2, KEYEVENTF_SCANCODE := 8, InputSize := 16 + A_PtrSize*3
   VarSetCapacity(INPUTS, InputSize * KeyArray.MaxIndex(), 0)
   addr := &INPUTS
   for k, v in KeyArray
      addr := NumPut( (v.event = "Up" ? KEYEVENTF_KEYUP : 0) | KEYEVENTF_SCANCODE | (v.sc >> 8)
            , NumPut(v.sc & 0xFF
            , NumPut(INPUT_KEYBOARD, addr + 0) + 2, "UShort"), "UInt" ) + 8 + A_PtrSize*2

   DllCall("SendInput", "UInt", KeyArray.MaxIndex(), "Ptr", &INPUTS, "Int", InputSize)
}
Many thanks!