模拟按键X秒

这是我用来在某个过程中模拟tab-keypress的代码:

[DllImport("user32.dll")] static extern bool PostMessage(IntPtr hWnd, UInt32 Msg, int wParam, int lParam); public Form1() { PostMessage(MemoryHandler.GetMainWindowHandle(), (int)KeyCodes.WMessages.WM_KEYDOWN, (int)KeyCodes.VKeys.VK_TAB, 0); InitializeComponent(); } 

有没有办法扩展它,以便按下(例如)1秒的键,而不是只是点击它?

请注意,我对阻止UI线程的Thread.Sleep()解决方案不感兴趣。

按住键时重复按键是键盘控制器内置的function。 键盘内置的微处理器。 8042微控制器是传统的选择,键盘设备驱动程序仍然以其名字命名。

所以,不,这不是由Windows完成的,PostMessage()不会为你做这件事。 这不是一个问题,您可以使用Timer模拟它。

如果您想模拟 Windows对消息的处理方式,您可能希望了解密钥重复率的速度。 可以在HKEY_CURRENT_USER\Control Panel\Keyboard\KeyboardSpeed 。 还有KeyboardDelay值。

Windows最初按下键时发送的是WM_KEYDOWNWM_CHAR 。 然后,如果在KeyboardDelay时间跨度之后仍然按下该键,则每个KeyboardSpeed都会重复WM_KEYDOWNWM_CHAR对,直到按下该键 – 此时发送WM_KEYUP

我建议使用Timer以特定频率发送消息。

更新:

例如:

 int keyboardDelay, keyboardSpeed; using (var key = Registry.CurrentUser.OpenSubKey(@"Control Panel\Keyboard")) { Debug.Assert(key != null); keyboardDelay = 1; int.TryParse((String)key.GetValue("KeyboardDelay", "1"), out keyboardDelay); keyboardSpeed = 31; int.TryParse((String)key.GetValue("KeyboardSpeed", "31"), out keyboardSpeed); } maxRepeatedCharacters = 30; // repeat char 30 times var startTimer = new System.Windows.Forms.Timer {Interval = keyboardSpeed}; startTimer.Tick += startTimer_Tick; startTimer.Start(); var repeatTimer = new System.Windows.Forms.Timer(); repeatTimer.Interval += keyboardDelay; repeatTimer.Tick += repeatTimer_Tick; //... private static void repeatTimer_Tick(object sender, EventArgs e) { PostMessage(MemoryHandler.GetMainWindowHandle(), (int)KeyCodes.WMessages.WM_KEYDOWN, (int)KeyCodes.VKeys.VK_TAB, 0); PostMessage(MemoryHandler.GetMainWindowHandle(), (int)KeyCodes.WMessages.WM_CHAR, (int)KeyCodes.VKeys.VK_TAB, 0); counter++; if (counter > maxRepeatedCharacters) { Timer timer = sender as Timer; timer.Stop(); } } private static void startTimer_Tick(object sender, EventArgs eventArgs) { Timer timer = sender as Timer; timer.Stop(); PostMessage(MemoryHandler.GetMainWindowHandle(), (int)KeyCodes.WMessages.WM_KEYDOWN, (int)KeyCodes.VKeys.VK_TAB, 0); PostMessage(MemoryHandler.GetMainWindowHandle(), (int)KeyCodes.WMessages.WM_CHAR, (int)KeyCodes.VKeys.VK_TAB, 0); } 

我会在一个线程中执行它,用于睡眠和不阻塞UI线程。 看这个:

 System.Threading.Thread KeyThread = new System.Threading.Thread(() => { //foreach process // press key now PostMessage(MemoryHandler.GetMainWindowHandle(), (int)KeyCodes.WMessages.WM_KEYDOWN, (int)KeyCodes.VKeys.VK_TAB, 0); System.Threading.Thread.Sleep(1000); // wait 1 second //foreach process // release keys again PostMessage(MemoryHandler.GetMainWindowHandle(), (int)KeyCodes.WMessages.WM_KEYUP, (int)KeyCodes.VKeys.VK_TAB, 0); }); 

当然,你必须开始它。

我不确定你想要实现什么,但下面是我用来模拟使用SendInput输入文本的函数。

如果你稍微改变一下来从新线程最后调用SendInput,然后用计时器分离出down和up事件,那么它能实现你需要的吗?

 [DllImport("user32.dll", SetLastError = true)] static extern UInt32 SendInput(UInt32 numberOfInputs, INPUT[] inputs, Int32 sizeOfInputStructure); public enum InputType : uint { MOUSE = 0, KEYBOARD = 1, HARDWARE = 2, } struct INPUT { public UInt32 Type; public MOUSEKEYBDHARDWAREINPUT Data; } struct KEYBDINPUT { public UInt16 Vk; public UInt16 Scan; public UInt32 Flags; public UInt32 Time; public IntPtr ExtraInfo; } public enum KeyboardFlag : uint // UInt32 { EXTENDEDKEY = 0x0001, KEYUP = 0x0002, UNICODE = 0x0004, SCANCODE = 0x0008, } public static void SimulateTextEntry(string text) { if (text.Length > UInt32.MaxValue / 2) throw new ArgumentException(string.Format("The text parameter is too long. It must be less than {0} characters.", UInt32.MaxValue / 2), "text"); var chars = UTF8Encoding.ASCII.GetBytes(text); var len = chars.Length; INPUT[] inputList = new INPUT[len * 2]; for (int x = 0; x < len; x++) { UInt16 scanCode = chars[x]; var down = new INPUT(); down.Type = (UInt32)InputType.KEYBOARD; down.Data.Keyboard = new KEYBDINPUT(); down.Data.Keyboard.Vk = 0; down.Data.Keyboard.Scan = scanCode; down.Data.Keyboard.Flags = (UInt32)KeyboardFlag.UNICODE; down.Data.Keyboard.Time = 0; down.Data.Keyboard.ExtraInfo = IntPtr.Zero; var up = new INPUT(); up.Type = (UInt32)InputType.KEYBOARD; up.Data.Keyboard = new KEYBDINPUT(); up.Data.Keyboard.Vk = 0; up.Data.Keyboard.Scan = scanCode; up.Data.Keyboard.Flags = (UInt32)(KeyboardFlag.KEYUP | KeyboardFlag.UNICODE); up.Data.Keyboard.Time = 0; up.Data.Keyboard.ExtraInfo = IntPtr.Zero; // Handle extended keys: // If the scan code is preceded by a prefix byte that has the value 0xE0 (224), // we need to include the KEYEVENTF_EXTENDEDKEY flag in the Flags property. if ((scanCode & 0xFF00) == 0xE000) { down.Data.Keyboard.Flags |= (UInt32)KeyboardFlag.EXTENDEDKEY; up.Data.Keyboard.Flags |= (UInt32)KeyboardFlag.EXTENDEDKEY; } inputList[2*x] = down; inputList[2*x + 1] = up; } var numberOfSuccessfulSimulatedInputs = SendInput((UInt32)len*2, inputList, Marshal.SizeOf(typeof(INPUT))); } 

不知道你在使用PostMessage做什么,但从这里修改了一些代码: SendKey.Send()不工作

  [DllImport("user32.dll", SetLastError = true)] static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, UIntPtr dwExtraInfo); public static void PressKey(Keys key, bool up) { const int KEYEVENTF_EXTENDEDKEY = 0x1; const int KEYEVENTF_KEYUP = 0x2; if (up) { keybd_event((byte)key, 0x45, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, (UIntPtr)0); } else { keybd_event((byte)key, 0x45, KEYEVENTF_EXTENDEDKEY, (UIntPtr)0); } } void TestProc() { PressKey(Keys.Tab, false); Thread.Sleep(1000); PressKey(Keys.Tab, true); } 

也许这对你有用。 它只是一键下来,然后是一个关键与之间的睡眠。 您甚至可以进一步添加此值,并传递一个时间值,表示您希望密钥保持多久。

在物理键盘上按住某个键时,会将重复的击键传递到活动窗口。 这不是内置于键盘中,而是Windowsfunction。

您可以按顺序执行以下步骤来模拟此操作:

  1. 发送keydown消息。
  2. 以30 ms的间隔运行计时器(Windows中的默认设置,可通过“轻松访问”设置进行更改),在每次打勾时向窗口发送按键消息。
  3. 发送密钥消息。