【问题标题】:How to repeat key strokes with SendInput?如何使用 SendInput 重复击键?
【发布时间】:2022-03-08 00:28:48
【问题描述】:

我正在用 VC++ 编写一个小工具来记录击键以便稍后重放它们,一个宏记录器。它已经很好地工作了,使用一个键盘钩子函数来读取每个按键按下和释放事件。播放与 SendInput() 函数一起使用,并且通常也可以正常工作 - 除了重复击键。每次松开后按几次键是没有问题的。但是按住它,对于重复输入的字符,可以记录但只能在某些应用程序中重放。有些接受并多次输入字符,有些只输入一次。 (它是可重现的。)宏记录器本身也通过它的监控钩子在回放期间看到按住的键被按下了一次。

那么,我怎样才能让 SendInput 发送单个键的多个后续击键,而不在两者之间添加我自己的键释放事件?发送 [press] [press] [press] ... [release] 序列并不总是有效。

【问题讨论】:

    标签: c++ winapi keyboard hook


    【解决方案1】:

    您可以在一次 SendInput 调用中发送多个键,但您仍需要在每个字符上设置键设置标志,以便在每种类型的击键上获得相同的结果。

    如果你需要发送“aa”,你可以这样做。

    INPUT input[4];
    
    input[0].type = INPUT_KEYBOARD;
    input[0].ki.wVk = 0;
    input[0].ki.wScan = 'a';
    input[0].ki.dwFlags = 0;
    
    input[1].type = INPUT_KEYBOARD;
    input[1].ki.wVk = 0;
    input[1].ki.wScan = 'a';
    input[1].ki.dwFlags = KEYEVENTF_KEYUP;
    
    input[2].type = INPUT_KEYBOARD;
    input[2].ki.wVk = 0;
    input[2].ki.wScan = 'a';
    input[2].ki.dwFlags = 0;
    
    input[3].type = INPUT_KEYBOARD;
    input[3].ki.wVk = 0;
    input[3].ki.wScan = 'a';
    input[3].ki.dwFlags = KEYEVENTF_KEYUP;
    
    SendInput(4, input, sizeof(INPUT));
    

    【讨论】:

    • 是的,我认为这就是我需要做的。但奇怪的是,从钩子函数中看不到那些 key_up 事件。因此,如果您使用的是真正的键盘,它实际上只发送 (down, down, up) 而不是 (down, up, down, up)。我希望我能以与记录事件相同的方式发送事件。接下来的问题是在什么关键事件之后插入 key_up 事件。显然,当按住 Alt 键,然后按住一个字母时,不希望插入 key_ups,因为它可能会聚焦菜单或其他东西。
    • 谢谢@Jan,顺便说一句,你是说 .Itype 还是 .type?我无法找到关于 .Itype 的 C++ 参考资料,但 .Itype 提供了 delphi 代码。
    • 如果你在 Delphi 中这样做,你需要设置input[n].Itype := INPUT_KEYBOARDtype 是 Delphi 中的保留关键字,因此将该字段重命名为 Itype
    • 啊,我看到了@Jan,谢谢你的信息。
    【解决方案2】:

    许多人在 keyup 和 keydown 调用被“解雇”或“丢弃”时遇到问题,许多人通过在两者之间放置一小段缓冲时间来解决他们的问题,以确保所有命令都被传输:

    sendinput  alt keydown
    sendinput  3  keydown
    sleep 50
    sendinput 3 keyup
    sendinput alt keyup
    

    还有一个SendInput.SendWait 命令....

    干杯

    【讨论】:

    • 不幸的是,SendInput 不允许延迟。您只需将要发送的关键事件作为 INPUT[] 提供给它,它就会立即发送它们——在一个片段中,它们之间没有其他事件。 SendWait 是一种 .NET 方法,我在这里没有使用 .NET。此外,“等待”不会影响实际的 SendInput 调用,如 .NET Reflector 所示。
    • (超出限制)一次发送密钥总是有效的,这里没问题。重复发送它并不总是有效,就像你的语法一样:sendinput 3 keydown;发送输入 3 按键;发送输入 3 按键;发送输入 3 键。 (这是钩子函数报告用户重复击键的顺序。)
    【解决方案3】:

    据我所知,它的工作方式是,如果在一段时间内(重复延迟)收到按键按下事件而没有按键按下事件,则按键被视为“重复”,直到按键按下.

    由于发送 press、press、press、release 并不总是有效,您是否尝试过记录按下键和按下键之间的时间,然后在同一时间重复命令?它是实时的,但它应该以这种方式触发相同的重复动作。

    我想不出任何其他方法可以得到与原始按键相同数量的“按键重复”,因为这些按键没有记录为单独的按键。

    【讨论】:

    • 对不起,我不明白你的意思。我正在将我从挂钩函数记录的确切事件发送到 SendInput()。该数据结构中有一个时间成员,但它似乎没有任何作用。
    • 时间成员是时间戳,而不是持续时间。您想模拟用户按住一个键 350 毫秒,然后您向下发送一个键,等待 350 毫秒,然后向上发送一个键。
    【解决方案4】:

    我不确定您的 确切 击键顺序,但我最近遇到了类似的问题。我编写了一个小型 Win32 工具 [1] 来在按下全局快捷键时发送键。当字符重复时,例如“aaabc”,重复的字符会丢失。我尝试了很多 KeyDown 和 KeyUp 的组合,但重复的字符总是丢失。

    然后我发现了这篇博文:https://batchloaf.wordpress.com/2014/10/02/using-sendinput-to-type-unicode-characters/

    虽然作者没有专门讨论重复字符,但它启发了我尝试仅使用 单个 INPUT 结构的 SendInput()。这种技术对我来说效果很好。

    简而言之:

    1. 调用SendInput(),只使用一个INPUT 结构。
    2. 要模拟单个键入的(常规)键,例如 z,试试这个:
      • 发送单个 KeyDown 事件:ki.dwFlags = KEYEVENTF_UNICODE
      • 发送单个 KeyUp 事件:ki.dwFlags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP

    [1]https://github.com/kevinarpe/win32/tree/master/send_input

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-04-20
      • 2015-08-22
      • 1970-01-01
      • 2012-02-16
      • 1970-01-01
      相关资源
      最近更新 更多