C#p/Invoke如何在DirectX游戏中使用SendInput模拟keyPRESS事件

本文关键字:SendInput 模拟 事件 keyPRESS 游戏 Invoke DirectX C#p | 更新日期: 2023-09-27 18:24:15

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

我已经设法用模拟了keydown事件

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)));

和按键事件使用:

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游戏中的按键按下/保持事件?

C#p/Invoke如何在DirectX游戏中使用SendInput模拟keyPRESS事件

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

现在我认为这在很大程度上取决于你试图"愚弄"的游戏/3d应用程序的具体情况。

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

然而,出于某种奇怪的原因,虽然我可以在窗口恢复焦点后第一次让它发挥作用,但在那之后,我根本无法让活动重复启动。

这让我很困惑,直到最后我在这里发帖陈述重复击键是键盘控制器的一项功能,而不是Windows或SendInput的功能。你当然可以用计时器来模拟它,重复调用SendInput()。

这让我想到,与其只发送一个击键或两个输入(一个扩展键向下,另一个向上),为什么不尝试像硬件设备可能发送的那样发送输入流(我实际上不确定它是否发送了,但当时这似乎都是个好主意)

所以最终,出现了故障(主要是试图发送X数量的单个INPUT1),我想到的是:

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持有钥匙的方式,就像人类持有钥匙一样。

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

使用这个代码,我已经能够模拟长期以来备受追捧的键盘按键被持有事件。我希望这将帮助其他一直在努力实现这一效果的人。我希望这对其他人有效。我希望比我更倾向于技术的人能够解释硬件实际上是如何模拟输入流中的键控事件的,以及如何使发送输入更紧密地进行同样的操作。