SendInput() 不等于在 C++ 中手动按下键盘上的键?

Posted

技术标签:

【中文标题】SendInput() 不等于在 C++ 中手动按下键盘上的键?【英文标题】:SendInput() not equal to pressing key manually on keyboard in C++? 【发布时间】:2013-09-05 22:37:47 【问题描述】:

我想编写一个 c++ 代码来模拟按下键盘键“A”:

// Set up a generic keyboard event.
ip.type = INPUT_KEYBOARD;
ip.ki.wScan = 0; // hardware scan code for key
ip.ki.time = 0;
ip.ki.dwExtraInfo = 0;

// Press the "..." key
ip.ki.wVk = code; // virtual-key code for the "a" key
ip.ki.dwFlags = 0; // 0 for key press
SendInput(1, &ip, sizeof(INPUT));

// Release the "..." key
ip.ki.dwFlags = KEYEVENTF_KEYUP; // KEYEVENTF_KEYUP for key release
SendInput(1, &ip, sizeof(INPUT));

当我启动其他程序并等待我的程序执行时,它工作正常,单击“A”并且第一个程序对其作出反应。但是我发现在另一个应用程序中我的操作被某种方式阻止了(我可以手动按键盘上的“A”,但使用我的程序不会导致任何操作)。

那么,我可以做些什么来使从程序中按“A”与手动按“A”更加相同(这样第二个程序就不会识别出它是从程序中调用的)?

我没有第二个程序的源代码,也不知道它是如何识别“A”不是手动按下的。

我确定我想对我的代码做出反应的窗口是前台,接收并阻止我的密钥(因此它可以确定事件不是来自用户而是来自程序)。

【问题讨论】:

SendInput 的发明是为了解决在向keybd_event 单独呼叫中发送单独键盘消息的问题。不要滥用SendInput,通过一个INPUT 结构多次调用它。构建一组INPUT 结构并一次性发送它们。 我也使用了 keybd_event ,结果相同。仍然感谢您指出一些提示。 我怀疑在您提供有关此“第二个程序”的更多详细信息之前,这个问题会出现任何问题。 OldNewThing 上有一个 few related 帖子可能会引起您的兴趣... afaik。似乎其他程序总是检查发件人的 hwnd。这是一种保护。 【参考方案1】:

您也可以使用 SendInput() 发送硬件扫描码(与 DirectInput 可能会忽略的虚拟扫描码相反)。它的文档记录很差,但 SendInput() 确实可以绕过 DirectInput。 Eric 的解决方案不起作用的原因是他设置了硬件扫描码,但最终使用了虚拟扫描码(通过将 dwFlags 设置为 0 并将 wVk 设置为非零)。

本质上,做一个你想要设置的按键:

ip.ki.dwFlags = KEYEVENTF_SCANCODE;

要释放密钥,设置:

ip.ki.dwFlags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP;

下面是一个完整的工作示例,它会打印字母“a”。可以找到其他扫码here。

#define WINVER 0x0500
#include <windows.h>

using namespace std;

int main()


    //Structure for the keyboard event
    INPUT ip;

    Sleep(5000);

    //Set up the INPUT structure
    ip.type = INPUT_KEYBOARD;
    ip.ki.time = 0;
    ip.ki.wVk = 0; //We're doing scan codes instead
    ip.ki.dwExtraInfo = 0;

    //This let's you do a hardware scan instead of a virtual keypress
    ip.ki.dwFlags = KEYEVENTF_SCANCODE;
    ip.ki.wScan = 0x1E;  //Set a unicode character to use (A)

    //Send the press
    SendInput(1, &ip, sizeof(INPUT));

    //Prepare a keyup event
    ip.ki.dwFlags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP;
    SendInput(1, &ip, sizeof(INPUT));



    return 0;

注意:您可以通过向 SendInput() 传递一个 INPUT 结构数组来组合按键(例如,对于 A,shift + a)。

【讨论】:

这种方式也可以模拟鼠标点击吗? 是的。使用 ip.type = INPUT_MOUSE。 好的。但是我需要作为 wScan 参数传递什么?是否有代表鼠标点击的 Unicode 字符? 不完全。 INPUT 结构是一个联合体,.ki 是用于键盘输入的。相反,您希望使用 .mi 字段,这是一个包含鼠标位置、鼠标事件(滚轮、左键单击等)和持续时间的结构。查看this msdn page 尽管我知道得更多,但我还是搜索了 unicode 字符而不是扫描代码来查找我需要的密钥。希望此链接可以避免其他人暂时白痴:philipstorr.id.au/pcbook/book3/scancode.htm【参考方案2】:

您经常需要设置扫描码:

// Set up a generic keyboard event.
ip.type = INPUT_KEYBOARD;
ip.ki.wScan = MapVirtualKey(code, MAPVK_VK_TO_VSC); // hardware scan code for key
ip.ki.time = 0;
ip.ki.dwExtraInfo = 0;

// Press the "..." key
ip.ki.wVk = code; // virtual-key code for the "a" key
ip.ki.dwFlags = 0; // 0 for key press
SendInput(1, &ip, sizeof(INPUT));

按照 IInspectable 的建议构建一个数组也是绝对可行的方法。

【讨论】:

感谢您的提示。不幸的是,它也不起作用。我想编写游戏机器人,但我的程序仍然只能在聊天窗口中工作(在我通过单击激活该窗口后,它可以将字符放入聊天窗口),但在游戏中(聊天)键停止工作(我相信是某种过滤器决定输入来自程序而不是用户)。 @PolGraphic 游戏可能正在使用 DirectInput 检索键盘输入。 DirectInput 直接与驱动程序对话,这使得注入相当成问题。游戏区分小写A和大写A吗? 当我使用它通过 WSAD 移动字符时(我不能从代码中做到这一点,只有键盘上的手动按键有效),在游戏聊天窗口中,小写和大写没有区别(我可以用我的代码来影响),它会有所不同。 这意味着游戏很可能正在使用 DirectInput。注入可能仍然是可能的,但要困难得多;您需要打开原始键盘设备并发送一些 IOCTL 以注入原始扫描码。我对这些 API 不是很熟悉,所以你只能靠自己......【参考方案3】:

如果您正在寻找创建游戏机器人,您是否看过 AutoHotKey 程序? http://www.autohotkey.com/

它提供了一种脚本语言,让您可以完成很多与“机器人”创建相关的任务,而且比尝试用 C++ 完成所有这些任务要容易得多

(当我所有的家人都迫使我创建一个帐户时,它确实为我播放了 Farmville)

【讨论】:

如果游戏是 3D,您可能想看看这个 - autohotkey.com/board/topic/… 这些应用程序当然很棒。但他们中的大多数都被游戏识别并导致游戏关闭或更糟的是让你被禁止。所以我更喜欢编写自己的应用程序,它们永远不会得到认可。

以上是关于SendInput() 不等于在 C++ 中手动按下键盘上的键?的主要内容,如果未能解决你的问题,请参考以下文章

在 Node-FFI 中使用 SendInput

时间:2019-04-10 标签:c++win32SendInput()

INPUT / vector数组不适用于SendInput

读取 UTF-8 文件,使用 SendInput 将内容传递给其他应用程序

如何正确使用 SendInput() 在 C# 中模拟鼠标输入

使用 SendInput 模拟拖放操作