请问如何使用DllCall的方式模拟键盘按键?

遇到了问题?请先进行搜索(中文和英文),然后在此提问

Moderators: tmplinshi, arcticir

killmatt01
Posts: 20
Joined: 25 Mar 2021, 00:12
Contact:

请问如何使用DllCall的方式模拟键盘按键?

Post by killmatt01 » 29 Jul 2021, 20:31

目前已经测试成功一种方式:

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)
但由于这个函数(keybd_event)已经被取代了,我想尝试最新的SendInput函数但遇到了问题
尝试如下:

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, A_PtrSize, "UShort")
    NumPut(ScanCode, Key_Down, A_PtrSize+4, "UShort")

    NumPut(INPUT_KEYBOARD, Key_Up, "UInt")
    NumPut(VirtualKey, Key_Up, A_PtrSize, "UShort")
    NumPut(ScanCode, Key_Up, A_PtrSize+4, "UShort")

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

    NumPut(KEYEVENTF_KEYUP, Key_Up, 2*A_PtrSize+4*3, "UInt")
    DllCall("SendInput", "UInt", 1, "Ptr", &Key_Down, "Int", 40)
    sleep, 100
    VarSetCapacity(Key_Up, 0)
}
至于尝试DllCall方式的缘由:我当然知道可以直接用

Code: Select all

key := "Space" ;等等等等
Send, {%key% Down}
Send, {%key% Up}
来模拟,但这种方式在非管理员权限的窗口里有些慢,在我电脑上send函数每行需要40ms,哪怕

Code: Select all

SetBatchLines, -1
SetKeyDelay, -1, -1
因为在此寻求帮助,提前感谢!
killmatt01
Posts: 20
Joined: 25 Mar 2021, 00:12
Contact:

Re: 请问如何使用DllCall的方式模拟键盘按键?

Post by killmatt01 » 04 Aug 2021, 20:09

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;    -> 8
         HARDWAREINPUT hi;
        };
  WORD wVk;                -> 8
  WORD wScan;              -> 16
  DWORD dwFlags;           -> 24
  DWORD time;              -> 28
  ULONG_PTR dwExtraInfo;   -> 36
*/
;==================================================================================
;convertions
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
}
tooltip将展示0
ps:
如果函数返回零,则输入已被另一个线程阻塞。
请问有什么方式解决吗?
User avatar
miozus
Posts: 15
Joined: 08 Sep 2021, 21:53

Re: 请问如何使用DllCall的方式模拟键盘按键?

Post by miozus » 08 Sep 2021, 23:08

:think:

你调用 DllCall 的时候,已经超过大部分 AHK 玩家了。

追求超低延迟,并保证多线程;

这听起来,不如用 C / Java 语言实现算法提供接口,最后 AHK 粘合调用它?
Post Reply

Return to “请求帮助”