如何阻止子进程模拟用户键盘输入或重定向其输入流?
解决子进程模拟回车键导致主程序提前退出的方案
方案1:重定向子进程的标准输入到空流
如果子进程是通过往自身标准输入写入回车键引发的问题,直接重定向它的标准输入并关闭,就能切断输入流向主进程控制台的路径。修改代码如下:
static void Main(string[] args) { Process process = new Process(); ProcessStartInfo startInfo = new ProcessStartInfo() { FileName = @"D:\PathTo\WorkerProcess.exe", Arguments = "-misc parameters", RedirectStandardOutput = true, RedirectStandardInput = true, // 开启标准输入重定向 UseShellExecute = false, CreateNoWindow = true }; process.StartInfo = startInfo; process.Start(); // 关闭子进程的标准输入流,阻止它写入任何输入 process.StandardInput.Close(); process.WaitForExit(10000); string processConsoleOutput = process.StandardOutput.ReadToEnd(); process.Close(); Console.WriteLine("Press Enter to exit."); Console.ReadLine(); }
方案2:让子进程在独立桌面会话运行
如果子进程是调用Windows API(比如SendInput)直接向当前桌面发送全局键盘事件,重定向流就起不到作用了。这种情况下,可以给子进程创建一个独立的临时桌面,让它的输入完全隔离,不会影响主进程的控制台:
using System; using System.Diagnostics; using System.Runtime.InteropServices; class Program { [DllImport("user32.dll", SetLastError = true)] private static extern IntPtr CreateDesktop(string lpszDesktop, IntPtr lpszDevice, IntPtr pDevmode, int dwFlags, uint dwDesiredAccess, IntPtr lpsa); [DllImport("user32.dll", SetLastError = true)] private static extern bool CloseDesktop(IntPtr hDesktop); [DllImport("user32.dll")] private static extern bool SetThreadDesktop(IntPtr hDesktop); [DllImport("kernel32.dll")] private static extern uint GetLastError(); const int DESKTOP_CREATEWINDOW = 0x0002; const int DESKTOP_SWITCHDESKTOP = 0x0100; const uint GENERIC_ALL = 0x10000000; static void Main(string[] args) { // 创建临时桌面 IntPtr tempDesktop = CreateDesktop("TempWorkerDesktop", IntPtr.Zero, IntPtr.Zero, 0, GENERIC_ALL, IntPtr.Zero); if (tempDesktop == IntPtr.Zero) { Console.WriteLine($"创建桌面失败,错误码:{GetLastError()}"); return; } // 将当前线程切换到临时桌面 if (!SetThreadDesktop(tempDesktop)) { Console.WriteLine($"切换线程桌面失败,错误码:{GetLastError()}"); CloseDesktop(tempDesktop); return; } Process process = new Process(); ProcessStartInfo startInfo = new ProcessStartInfo() { FileName = @"D:\PathTo\WorkerProcess.exe", Arguments = "-misc parameters", RedirectStandardOutput = true, UseShellExecute = false, CreateNoWindow = true }; process.StartInfo = startInfo; process.Start(); process.WaitForExit(10000); string processConsoleOutput = process.StandardOutput.ReadToEnd(); process.Close(); // 切回原桌面并销毁临时桌面 SetThreadDesktop(IntPtr.Zero); CloseDesktop(tempDesktop); Console.WriteLine("Press Enter to exit."); Console.ReadLine(); } }
方案3:拦截全局键盘事件(兜底方案)
如果前两种方法都无效,可以在主进程中挂载全局键盘钩子,识别并忽略子进程触发的回车键。这个方法复杂度较高,且可能影响系统内其他程序的键盘输入,仅作为兜底方案使用:
using System; using System.Diagnostics; using System.Runtime.InteropServices; using System.Windows.Forms; class Program { private static IntPtr _hookId = IntPtr.Zero; private static int _workerProcessId; [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId); [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool UnhookWindowsHookEx(IntPtr hhk); [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam); [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern IntPtr GetModuleHandle(string lpModuleName); private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam); private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam) { if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN) { int vkCode = Marshal.ReadInt32(lParam); if (vkCode == (int)Keys.Return) { // 获取触发键盘事件的进程ID uint processId; GetWindowThreadProcessId(GetForegroundWindow(), out processId); if (processId == _workerProcessId) { // 忽略子进程触发的回车键 return (IntPtr)1; } } } return CallNextHookEx(_hookId, nCode, wParam, lParam); } [DllImport("user32.dll")] private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); [DllImport("user32.dll")] private static extern IntPtr GetForegroundWindow(); private const int WH_KEYBOARD_LL = 13; private const int WM_KEYDOWN = 0x0100; static void Main(string[] args) { // 设置全局键盘钩子 _hookId = SetHook(HookCallback); Process process = new Process(); ProcessStartInfo startInfo = new ProcessStartInfo() { FileName = @"D:\PathTo\WorkerProcess.exe", Arguments = "-misc parameters", RedirectStandardOutput = true, UseShellExecute = false, CreateNoWindow = true }; process.StartInfo = startInfo; process.Start(); _workerProcessId = process.Id; process.WaitForExit(10000); string processConsoleOutput = process.StandardOutput.ReadToEnd(); process.Close(); Console.WriteLine("Press Enter to exit."); Console.ReadLine(); // 卸载钩子 UnhookWindowsHookEx(_hookId); } private static IntPtr SetHook(LowLevelKeyboardProc proc) { using (Process curProcess = Process.GetCurrentProcess()) using (ProcessModule curModule = curProcess.MainModule) { return SetWindowsHookEx(WH_KEYBOARD_LL, proc, GetModuleHandle(curModule.ModuleName), 0); } } }
内容的提问来源于stack exchange,提问作者Anketam
相关产品推荐
相关产品推荐

