0

我经常为各种机器人或其他 GUI 自动化程序模拟键盘按下事件而苦苦挣扎。

我设法使用以下方法模拟按键事件:

INPUT[] kInput = new INPUT[1];
kInput[j].type = SendInputEventType.InputKeyboard;
kInput[j].mkhi.ki.wVk = 0;
kInput[j].mkhi.ki.wScan = (ushort) MapVirtualKey((uint) Keys.D5, 0);
kInput[j].mkhi.ki.dwFlags = KeyboardEventFlags.SCANCODE;
kInput[j].mkhi.ki.time = 0;
kInput[j].mkhi.ki.dwExtraInfo = IntPtr.Zero;
SendInput(1, kInput, Marshal.SizeOf(typeof(INPUT)));

或者

INPUT[] kInput = new INPUT[1];
kInput[1].type = SendInputEventType.InputKeyboard;
kInput[1].mkhi.ki.wVk = '5';
kInput[1].mkhi.ki.wScan = 0;
kInput[1].mkhi.ki.dwFlags = 0;
kInput[1].mkhi.ki.time = 0;
kInput[1].mkhi.ki.dwExtraInfo = IntPtr.Zero;
SendInput(1, kInput, Marshal.SizeOf(typeof(INPUT)));

和 keyup 事件使用:

kInput[1].type = SendInputEventType.InputKeyboard;
kInput[1].mkhi.ki.wVk = 0;
kInput[1].mkhi.ki.wScan = (ushort)MapVirtualKey((uint)Keys.D5, 0);
kInput[1].mkhi.ki.dwFlags = KeyboardEventFlags.KEYUP;
kInput[1].mkhi.ki.time = 0;
kInput[1].mkhi.ki.dwExtraInfo = IntPtr.Zero;
SendInput(1, kInput, Marshal.SizeOf(typeof(INPUT)));

或者

kInput[1].type = SendInputEventType.InputKeyboard;
kInput[1].mkhi.ki.wVk = '5';
kInput[1].mkhi.ki.wScan = 0;
kInput[1].mkhi.ki.dwFlags = KeyboardEventFlags.KEYUP;
kInput[1].mkhi.ki.time = 0;
kInput[1].mkhi.ki.dwExtraInfo = IntPtr.Zero;
SendInput(1, kInput, Marshal.SizeOf(typeof(INPUT)));

虽然我尝试将两者结合为:

//key down
INPUT kInput = new INPUT[2];
kInput[0].type = SendInputEventType.InputKeyboard;
kInput[0].mkhi.ki.wVk = 0;
kInput[0].mkhi.ki.wScan = (ushort) MapVirtualKey((uint) Keys.D5, 0);
kInput[0].mkhi.ki.dwFlags = KeyboardEventFlags.SCANCODE;
kInput[0].mkhi.ki.time = 0;
kInput[0].mkhi.ki.dwExtraInfo = IntPtr.Zero;

//Key Up
kInput[1].type = SendInputEventType.InputKeyboard;
kInput[1].mkhi.ki.wVk = 0;
kInput[1].mkhi.ki.wScan = (ushort)MapVirtualKey((uint)Keys.D5, 0);
kInput[1].mkhi.ki.dwFlags = KeyboardEventFlags.KEYUP;
kInput[1].mkhi.ki.time = 0;
kInput[1].mkhi.ki.dwExtraInfo = IntPtr.Zero;

//sendinput
SendInput(1, kInput, Marshal.SizeOf(typeof(INPUT)));

我已经取得了不同程度的成功,但我实际上从来没有能力模拟一个按键按住/按下事件,在这种情况下,DirectX(或其他 3d)游戏会像使用物理键盘的人那样看到我的输入......

如何使用 SendInput 模拟 DirectX 游戏所见的按键/按住事件?

4

2 回答 2

3

要在 directX 游戏中模拟键盘事件,您需要一个键盘驱动程序的包装器。我遇到了同样的问题,并且这些方法(SendInput、SendKeys 类)都对我没有帮助。尝试使用这个包装器,对我来说它是成功的。

于 2014-03-19T06:56:03.750 回答
1

现在我认为这在很大程度上取决于你试图“愚弄”的游戏/3d 应用程序的细节。

对于我第一次尝试欺骗我正在开发的特定游戏的一些奇怪情况,只要游戏窗口本身重新获得焦点,它就会只接受一个 SCANCODE 作为按键来切换技能。(IE 你打开了一个网络浏览器,你的应用程序切换到游戏窗口,然后触发 SCANCODE 事件)。

然而,出于某种奇怪的原因,虽然我可以在窗口重新获得焦点后第一次让它工作,但在那之后我根本无法让事件重复触发。

这让我感到困惑,直到最终我在这里发表了一篇文章, 指出“重复击键是键盘控制器的一个功能,而不是 Windows 或 SendInput。你当然可以用计时器来模拟它,重复调用 SendInput()。”

这让我想到,与其只发送一个击键或两个输入(一个扩展键向下,另一个键向上),不如尝试以硬件设备可能的方式发送输入流(我不这样做)实际上肯定知道它是否确实如此,但当时这一切似乎都是个好主意)

所以最终,并且出现了失败(主要是尝试发送 X 数量的单个 INPUT 1),我想出的是:

INPUT[] kInput = new INPUT[30];
for (int j = 0; j < kInput.Length - 1; j++)
{
    //activate skill
    //INPUT[] kInput = new INPUT[2];
    kInput[j].type = SendInputEventType.InputKeyboard;
    kInput[j].mkhi.ki.wVk = 0;
    kInput[j].mkhi.ki.wScan = (ushort) MapVirtualKey((uint) Keys.D5, 0);
    kInput[j].mkhi.ki.dwFlags = KeyboardEventFlags.SCANCODE;
    kInput[j].mkhi.ki.time = 0;
    kInput[j].mkhi.ki.dwExtraInfo = IntPtr.Zero;
}
kInput[kInput.Length - 1].type = SendInputEventType.InputKeyboard;
kInput[kInput.Length - 1].mkhi.ki.wVk = 0;
kInput[kInput.Length - 1].mkhi.ki.wScan = (ushort)MapVirtualKey((uint)Keys.D5, 0);
kInput[kInput.Length - 1].mkhi.ki.dwFlags = KeyboardEventFlags.KEYUP;
kInput[kInput.Length - 1].mkhi.ki.time = 0;
kInput[kInput.Length - 1].mkhi.ki.dwExtraInfo = IntPtr.Zero;
SendInput((uint)kInput.Length, kInput, Marshal.SizeOf(typeof(INPUT)));

对于我目前试图“愚弄”它的游戏,这似乎对我来说足够好。这是我第一次能够以人类可能持有的方式模拟使用 SendInput 持有的键。

现在请注意,在我的示例中,我的输入数组的大小为 30。这不是 30,因为 30 是一个神奇的值,而是 30,因为 30 是我必须选择的第一个也是唯一的数字才能达到我想要的结果。

使用此代码,我已经能够模拟长期以来备受追捧的键盘键被按住事件。我希望这将有助于其他一直试图达到这种效果的人。我希望这对其他人有用。而且我希望比我更倾向于技术的人能够解释硬件如何实际模拟输入流中的键持有事件,以及如何使发送输入更接近地做同样的事情。

于 2013-07-22T06:13:48.810 回答