我如何发送ctrl + c到c#中的进程?
我正在编写一个命令行可执行文件的包装类。 这个EXE接受来自stdin的input,直到我在命令提示符shell中按ctrl + c,在这种情况下,它将基于input的输出打印到标准输出。 我想模拟ctrl + c按c#代码,发送杀死命令到一个.Net过程对象。 我已经尝试过调用Process.kill(),但是在进程的StandardOutput StreamReader中似乎没有给我任何东西。 可能有什么我不正确的做法? 这是我正在尝试使用的代码:
ProcessStartInfo info = new ProcessStartInfo(exe, args); info.RedirectStandardError = true; info.RedirectStandardInput = true; info.RedirectStandardOutput = true; info.UseShellExecute = false; Process p = Process.Start(info); p.StandardInput.AutoFlush = true; p.StandardInput.WriteLine(scriptcode); p.Kill(); string error = p.StandardError.ReadToEnd(); if (!String.IsNullOrEmpty(error)) { throw new Exception(error); } string output = p.StandardOutput.ReadToEnd();
但是输出总是空的,即使我手动运行exe时从stdout中获取数据。 编辑:这是c#2.0顺便说一句
其实我只是想出了答案。 谢谢你的答案,但事实certificate,我所要做的就是这样:
p.StandardInput.Close()
这导致我产生的程序完成从标准input读取和输出我所需要的。
@alonl:用户正试图包装一个命令行程序。 命令行程序没有消息泵,除非它们是专门创build的,即使是这样的情况,Ctrl + C在Windows环境应用程序中也没有相同的语义(默认情况下是复制)一个命令行环境(Break)。
我把它扔在一起。 CtrlCClient.exe只是简单地调用Console.ReadLine()并等待:
static void Main(string[] args) { ProcessStartInfo psi = new ProcessStartInfo("CtrlCClient.exe"); psi.RedirectStandardInput = true; psi.RedirectStandardOutput = true; psi.RedirectStandardError = true; psi.UseShellExecute = false; Process proc = Process.Start(psi); Console.WriteLine("{0} is active: {1}", proc.Id, !proc.HasExited); proc.StandardInput.WriteLine("\x3"); Console.WriteLine(proc.StandardOutput.ReadToEnd()); Console.WriteLine("{0} is active: {1}", proc.Id, !proc.HasExited); Console.ReadLine(); }
我的输出似乎做你想要的:
4080是活动的:是的 4080有效:错误
希望有所帮助!
(为了说明:\ x3是hex字符3的hex转义序列,它是ctrl + c,它不只是一个幻数);)
尽pipe使用GenerateConsoleCtrlEvent发送Ctrl + C信号是一个正确的答案,但需要大量的说明才能使其在不同的.NET应用程序types中工作。
如果你的.NET应用程序没有使用自己的控制台(WinForms / WPF / Windows Service / ASP.NET),那么基本stream程是:
- 将主要的.NET进程连接到你想要Ctrl + C的进程的控制台
- 通过SetConsoleCtrlHandler阻止主要.NET进程因Ctrl + C事件而停止
- 使用GenerateConsoleCtrlEvent生成当前控制台的控制台事件(processGroupId应该为零!使用发送p.SessionId的代码的答案将不起作用并且不正确)
- 从控制台断开连接并恢复主进程的Ctrl + C处理
下面的代码片段说明了如何做到这一点:
Process p; if (AttachConsole((uint)p.Id)) { SetConsoleCtrlHandler(null, true); try { if (!GenerateConsoleCtrlEvent(CTRL_C_EVENT,0)) return false; p.WaitForExit(); } finally { FreeConsole(); SetConsoleCtrlHandler(null, false); } return true; }
其中SetConsoleCtrlHandler,FreeConsole,AttachConsole和GenerateConsoleCtrlEvent是本机WinAPI方法:
internal const int CTRL_C_EVENT = 0; [DllImport("kernel32.dll")] internal static extern bool GenerateConsoleCtrlEvent(uint dwCtrlEvent, uint dwProcessGroupId); [DllImport("kernel32.dll", SetLastError = true)] internal static extern bool AttachConsole(uint dwProcessId); [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)] internal static extern bool FreeConsole(); [DllImport("kernel32.dll")] static extern bool SetConsoleCtrlHandler(ConsoleCtrlDelegate HandlerRoutine, bool Add); // Delegate type to be used as the Handler Routine for SCCH delegate Boolean ConsoleCtrlDelegate(uint CtrlType);
如果您需要从.NET控制台应用程序发送Ctrl + C,事情变得更加复杂。 方法将无法工作,因为AttachConsole在这种情况下返回false(主控制台应用程序已经有一个控制台)。 可以在AttachConsole调用之前调用FreeConsole,但结果是原来的.NET应用程序控制台将丢失,这在大多数情况下是不可接受的。
我对这种情况的解决scheme(这真的有效,并没有.NET主进程控制台的副作用):
- 创build小的支持.NET控制台程序,它接受来自命令行参数的进程标识,在AttachConsole调用之前用FreeConsole丢弃自己的控制台,并使用上述代码将Ctrl + C发送到目标进程
- 主要的.NET控制台进程只是在需要将Ctrl + C发送到另一个控制台进程时在新进程中调用此实用程序
好的,这是一个解决scheme。
发送Ctrl-C信号的方法是使用GenerateConsoleCtrlEvent。 然而,这个调用需要一个processGroupdID参数,并将Ctrl-C信号发送到组中的所有进程。 如果没有办法在.net中生成subprocess,而不是在父进程所在的进程组中,那么这样做可能没有问题。因此,当您发送GenerateConsoleCtrlEvent时,subprocess和你(家长)取得联系。 所以,你也需要捕获父项中的ctrl-c事件,然后确定是否要忽略它。
在我的情况下,我希望父母也能够处理Ctrl-C事件,所以我需要在控制台上的用户发送的Ctrl-C事件与父进程发送给孩子的事件之间分离。 我这样做只是hackishly设置/取消设置布尔标志,同时发送ctrl-c到孩子,然后检查父母的ctrl-c事件处理程序中的这个标志(即如果发送ctrl-c到子,然后忽略。 )
所以,代码看起来像这样:
//import in the declaration for GenerateConsoleCtrlEvent [DllImport("kernel32.dll", SetLastError=true)] static extern bool GenerateConsoleCtrlEvent(ConsoleCtrlEvent sigevent, int dwProcessGroupId); public enum ConsoleCtrlEvent { CTRL_C = 0, CTRL_BREAK = 1, CTRL_CLOSE = 2, CTRL_LOGOFF = 5, CTRL_SHUTDOWN = 6 } //set up the parents CtrlC event handler, so we can ignore the event while sending to the child public static volatile bool SENDING_CTRL_C_TO_CHILD = false; static void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e) { e.Cancel = SENDING_CTRL_C_TO_CHILD; } //the main method.. static int Main(string[] args) { //hook up the event handler in the parent Console.CancelKeyPress += new ConsoleCancelEventHandler(Console_CancelKeyPress); //spawn some child process System.Diagnostics.ProcessStartInfo psi = new System.Diagnostics.ProcessStartInfo(); psi.Arguments = "childProcess.exe"; Process p = new Process(); p.StartInfo = psi; p.Start(); //sned the ctrl-c to the process group (the parent will get it too!) SENDING_CTRL_C_TO_CHILD = true; GenerateConsoleCtrlEvent(ConsoleCtrlEvent.CTRL_C, p.SessionId); p.WaitForExit(); SENDING_CTRL_C_TO_CHILD = false; //note that the ctrl-c event will get called on the parent on background thread //so you need to be sure the parent has handled and checked SENDING_CTRL_C_TO_CHILD already before setting it to false. 1000 ways to do this, obviously. //get out.... return 0; }
尝试实际发送组合键Ctrl + C,而不是直接终止进程:
[DllImport("user32.dll")] public static extern int SendMessage( int hWnd, // handle to destination window uint Msg, // message long wParam, // first message parameter long lParam // second message parameter );
在MSDN上查找,你应该find你需要的地方,以发送Ctrl +组合键…我知道你需要发送Alt + Key的消息是WM_SYSTEMKEYDOWN和WM_SYSTEMKEYUP,不能告诉你关于Ctrl …