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

时间:2013-07-22 06:13:49

标签: c# pinvoke keypress sendinput

我经常为各种机器人或其他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)));

OR

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

OR

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游戏所看到的按键/按住事件?

2 个答案:

答案 0 :(得分:3)

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

答案 1 :(得分:0)

现在我认为其中很多实际上取决于您试图“傻瓜”的游戏/ 3d应用程序的细节。

对于我第一次试图欺骗特定游戏的一些奇怪的情况,我正在努力它只接受一个SCANCODE足够的按键来切换技能,只要游戏窗口本身重新获得焦点。 (例如,您打开了Web浏览器,您的应用程序切换到游戏窗口,然后触发SCANCODE事件)。

然而,由于一些奇怪的原因,虽然我可以在窗口重新获得焦点后第一次使用此功能,但在此之后我无法重复发射该事件。

这让我很困惑,直到最后我才发表here的帖子 说明 “重复击键是键盘控制器的一个功能,而不是Windows或SendInput。您当然可以使用计时器模拟它,重复调用SendInput()。”

这给了我的想法,不是只发送一个击键,或两个输入(一个扩展键向下,另一个键向上),为什么不尝试以硬件设备MIGHT(我的方式)发送输入流实际上并不确定它是否确实存在,但当时这似乎都是一个好主意)

所以最终,并且有失败(主要是尝试发送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是第一个也是唯一一个我必须选择的数字来实现结果我希望。

使用此代码,我已经能够模拟长期追捧的键盘键被保持事件。我希望这会帮助那些试图达到这种效果的人。我希望这适用于其他人。我希望那些技术上比我更倾向的人可以解释硬件如何实际模拟输入流中的一个关键事件,以及如何发送输入以更接近地做同样的事情。

相关问题