C#中如何向外部进程窗口发送键盘输入?
向外部进程发送键盘输入的解决方案
你已经通过以下代码启动并等待进程:
Process reprocessor = Process.Start(@"C:\Scripts\Tools\Reprocessor\reprocessor.exe"); // 等待进程启动 Process[] processes; do { processes = Process.GetProcessesByName("reprocessor"); Thread.Sleep(1000); } while (processes.Length == 0);
但使用SendMessage未成功,大概率是未定位到正确的窗口/控件句柄,或是未先激活目标窗口。下面是几种靠谱的实现方式:
方法1:使用SendKeys(简单易用,适合大多数场景)
SendKeys会向当前活动窗口发送按键,所以需要先将目标进程的窗口设为前台。
实现代码:
using System.Diagnostics; using System.Threading; using System.Windows.Forms; // 需要引用System.Windows.Forms程序集 // 获取进程的主窗口句柄 IntPtr mainWindowHandle = processes[0].MainWindowHandle; // 激活窗口 SetForegroundWindow(mainWindowHandle); Thread.Sleep(500); // 等待窗口激活 // 发送按键:例如Ctrl+Tab,再按数字键1 SendKeys.SendWait("^{TAB}"); // ^代表Ctrl键 SendKeys.SendWait("1");
需要添加Windows API声明:
[System.Runtime.InteropServices.DllImport("user32.dll")] private static extern bool SetForegroundWindow(IntPtr hWnd);
方法2:使用SendInput(底层模拟,不依赖前台窗口)
SendInput直接模拟硬件输入,不需要激活窗口,但实现稍复杂,适合需要后台操作的场景。
实现代码:
using System.Runtime.InteropServices; // 定义API结构和函数 [StructLayout(LayoutKind.Sequential)] private struct INPUT { public uint Type; public INPUTUNION Data; } [StructLayout(LayoutKind.Explicit)] private struct INPUTUNION { [FieldOffset(0)] public KEYBDINPUT Keyboard; } [StructLayout(LayoutKind.Sequential)] private struct KEYBDINPUT { public ushort Vk; public ushort Scan; public uint Flags; public uint Time; public IntPtr ExtraInfo; } [DllImport("user32.dll", SetLastError = true)] private static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize); // 模拟按下和释放按键的方法 private static void SimulateKey(ushort keyCode, bool isPress) { INPUT input = new INPUT(); input.Type = 1; // 键盘输入类型 input.Data.Keyboard.Vk = keyCode; input.Data.Keyboard.Flags = isPress ? 0 : 2; // 0=按下,2=释放 input.Data.Keyboard.Time = 0; input.Data.Keyboard.ExtraInfo = IntPtr.Zero; INPUT[] inputs = new INPUT[1]; inputs[0] = input; SendInput(1, inputs, Marshal.SizeOf(typeof(INPUT))); } // 使用示例:发送Ctrl+Tab,再按数字1 public static void SendKeysToProcess() { // 按下Ctrl SimulateKey((ushort)Keys.ControlKey, true); // 按下Tab SimulateKey((ushort)Keys.Tab, true); // 释放Tab SimulateKey((ushort)Keys.Tab, false); // 释放Ctrl SimulateKey((ushort)Keys.ControlKey, false); Thread.Sleep(200); // 按下数字1 SimulateKey((ushort)Keys.D1, true); SimulateKey((ushort)Keys.D1, false); }
方法3:正确使用PostMessage(需要定位目标控件句柄)
如果SendMessage无效,可能是因为你直接发送到了进程的主窗口,而实际接收输入的是窗口内的子控件。可以通过VS自带的Spy++工具找到目标控件的句柄,再发送消息。
实现代码:
[DllImport("user32.dll", CharSet = CharSet.Auto)] private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam); private const uint WM_KEYDOWN = 0x0100; private const uint WM_KEYUP = 0x0101; // 假设已找到目标控件的句柄targetHandle SendMessage(targetHandle, WM_KEYDOWN, (IntPtr)Keys.Tab, IntPtr.Zero); SendMessage(targetHandle, WM_KEYUP, (IntPtr)Keys.Tab, IntPtr.Zero);
额外注意:
- 如果目标进程是WinForms,主窗口句柄可能需要等待加载完成,可以在循环里补充判断:
while (processes[0].MainWindowHandle == IntPtr.Zero) { Thread.Sleep(500); processes[0].Refresh(); } SendKeys需要确保目标窗口处于前台,否则会发送到当前活动窗口;SendInput则可以在后台模拟,但部分场景下需要管理员权限。
内容的提问来源于stack exchange,提问作者gclark18
相关产品推荐
相关产品推荐

