如何将 ctrl+c 发送到 C# 中的进程?
我正在为命令行可执行文件编写一个包装类。该 exe 接受来自 stdin
的输入,直到我在命令提示符 shell 中按下 Ctrl+C
,在这种情况下,它会根据以下内容将输出打印到 stdout
:输入。我想在 C# 代码中模拟 Ctrl+C
按下,将终止命令发送到 .NET Process
对象。我尝试调用 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。
I'm writing a wrapper class for a command line executable. This exe accepts input from stdin
until I hit Ctrl+C
in the command prompt shell, in which case it prints output to stdout
based on the input. I want to simulate that Ctrl+C
press in C# code, sending the kill command to a .NET Process
object. I've tried calling Process.Kill()
, but that doesn't seem to give me anything in the process's StandardOutput
StreamReader
. Might there be anything I'm not doing right? Here's the code I'm trying to use:
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();
The output is always empty, even though I get data back from stdout
when I run the exe manually.
Edit: This is C# 2.0 by the way.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
尽管事实上使用
GenerateConsoleCtrlEvent()
发送 Ctrl+C 信号是正确的答案,但需要进行大量澄清才能使其正常工作不同的 .NET 应用程序类型。如果您的 .NET 应用程序不使用自己的控制台(Windows Forms/WPF/Windows Service/ASP.NET),则基本流程是: 将
SetConsoleCtrlHandler()
禁用信号处理,防止主 .NET 进程因 Ctrl+C 事件而停止。GenerateConsoleCtrlEvent()
为当前控制台生成控制台事件(processGroupId
应为零!答案为发送p. SessionId 将不起作用并且不正确)。
以下代码片段说明了如何执行此操作:
其中
SetConsoleCtrlHandler()
、FreeConsole()
、AttachConsole()
和GenerateConsoleCtrlEvent()
是本机 WinAPI 方法:请注意,等待目标进程响应(通常是等待进程退出)至关重要。否则,Ctrl+C 信号将保留在当前进程的输入队列中,并且当通过第二次调用
SetConsoleCtrlHandler()
恢复处理时,该信号将终止当前进程,而不是目标进程。如果您需要从 .NET 控制台应用程序发送 Ctrl+C,事情会变得更加复杂。上述方法将不起作用,因为在这种情况下
AttachConsole()
返回false
(主控制台应用程序已经有一个控制台)。可以在AttachConsole()
调用之前调用FreeConsole()
,但这样做会导致原始 .NET 应用控制台丢失,这在大多数情况下是不可接受的。这是我针对这种情况的解决方案;它可以工作并且对 .NET 主进程控制台没有副作用:
FreeConsole()
之前丢失自己的控制台>AttachConsole() 调用并使用上述代码将 Ctrl+C 发送到目标进程。SetConsoleCtrlHandler(null, true)
,并在之后调用SetConsoleCtrlHandler(null, false)
。否则你的主进程也会收到 Ctrl+C 并死亡Despite the fact that using
GenerateConsoleCtrlEvent()
for sending Ctrl+C signal is the right answer, it needs significant clarification to get it to work in different .NET application types.If your .NET application doesn't use its own console (Windows Forms/WPF/Windows Service/ASP.NET), the basic flow is:
SetConsoleCtrlHandler()
.GenerateConsoleCtrlEvent()
(processGroupId
should be zero! The answer with code that sendsp.SessionId
will not work and is incorrect).The following code snippet illustrates how to do that:
where
SetConsoleCtrlHandler()
,FreeConsole()
,AttachConsole()
andGenerateConsoleCtrlEvent()
are native WinAPI methods:Note that waiting for the targeted process to respond, typically by waiting for the process to exit, is critical. Otherwise, the Ctrl+C signal will remain in the current process's input queue and when handling is restored by the second call to
SetConsoleCtrlHandler()
, that signal will terminate the current process, rather than the targeted one.Things become more complex if you need to send Ctrl+C from .NET console application. The above approach will not work because
AttachConsole()
returnsfalse
in this case (the main console app already has a console). It is possible to callFreeConsole()
beforeAttachConsole()
call, but doing so will result in the original .NET app console being lost, which is not acceptable in most cases.Here is my solution for this case; it works and has no side effects for the .NET main process console:
FreeConsole()
before theAttachConsole()
call and sends Ctrl+C to the target process with code mentioned above.SetConsoleCtrlHandler(null, true)
before spawning the "killer"-process (from step 1) andSetConsoleCtrlHandler(null, false)
after. Else your main process will also receive Ctrl+C and die其实我刚刚找到了答案。谢谢你们的回答,但事实证明我所要做的就是这样:
这会导致我生成的程序完成从标准输入的读取并输出我需要的内容。
I've actually just figured out the answer. Thank you both for your answers, but it turns out that all i had to do was this:
which causes the program I've spawned to finish reading from stdin and output what i need.
@alonl:用户正在尝试包装命令行程序。命令行程序没有消息泵,除非它们是专门创建的,即使是这样,Ctrl+C 在命令行中也不具有相同的语义。 Windows 环境应用程序(默认情况下复制)就像在命令行环境中一样 (Break)。
我把这个放在一起。 CtrlCClient.exe 只需调用
Console.ReadLine()
并等待:我的输出似乎符合您的要求:
希望有帮助!
(澄清一下:
\x3
是十六进制字符 3 的十六进制转义序列,即 Ctrl+C。它不仅仅是一个神奇的数字.;))@alonl: The user is attempting to wrap a command-line program. Command-line programs don't have message pumps unless they are specifically created, and even if that was the case, Ctrl+C doesn't have the same semantics in a Windows-environment application (copy, by default) as it does in a command-line environment (Break).
I threw this together. CtrlCClient.exe simply calls
Console.ReadLine()
and waits:My output seems to do what you want:
Hope that helps!
(To clarify:
\x3
is the hex escape sequence for the hex character 3, which is Ctrl+C. It's not just a magic number. ;) )好的,这是一个解决方案。
发送Ctrl-C信号的方法是使用GenerateConsoleCtrlEvent。但是,此调用采用 processGroupdID 参数,并将 Ctrl-C 信号发送到组中的所有进程。如果不是因为无法在 .net 中生成与您(父进程)位于不同进程组中的子进程,那就没问题了。因此,当您发送GenerateConsoleCtrlEvent时,子进程都将处于不同的进程组中。您(家长)明白了。因此,您还需要捕获父级中的 Ctrl-C 事件,然后确定是否需要忽略它。
就我而言,我希望父级也能够处理 Ctrl-C 事件,因此我需要区分 Ctrl-C 用户在控制台上发送的事件,以及父进程发送给子进程的事件。我通过在将 Ctrl-C 发送给子级时简单地设置/取消设置布尔标志来做到这一点,然后在父级的 Ctrl< 中检查此标志。 /kbd>-C 事件处理程序(即,如果将 Ctrl-C 发送给子级,则忽略。)
因此,代码看起来像是这样像这样:
Ok, here is a solution.
The way to send the Ctrl-C signal is with GenerateConsoleCtrlEvent. HOWEVER, this call takes a processGroupdID parameter, and sends the Ctrl-C signal to all processes in the group. This would be fine if it weren't for the fact that there is no way spawn child process in .net that is in a different process group than you (the parent) are in. So, when you send the GenerateConsoleCtrlEvent, both the child AND YOU (THE PARENT) GET IT. So, you need to capture the Ctrl-C event in the parent too, and then determine if you ned to ignore it not.
In my case, I want the parent to be able to handle Ctrl-C events also, so I need to distnguish between Ctrl-C events sent by the user on the console, and those sent by the parent process to the child. I do this by just hackishly setting/unsetting a boolean flag while send the Ctrl-C to the child, and then checking for this flag in the parent's Ctrl-C event handler (ie. if send Ctrl-C to child, then ignore.)
So, the code would look something like this:
FWIW,就我而言,我想要的是从控制台进程创建一个子控制台进程(与此相关的 ffmpeg.exe)并支持干净的 CTRL-C 处理在我的进程和子进程中(当按下 CTRL-C 时 ffmpreg 正常退出,这是我想继续工作的一个很好的功能)
我在这里找不到任何解决方案正在工作,所以我只是互操作 Windows' CreateProcess 函数,它无需任何努力即可工作,CTRL-C 由子应用程序和父应用程序自动接收,输入和输出流是共享的,等等。我无法使用标准 .NET Process 类重现此类代码:
FWIW, in my case, what I wanted is, from a console process, create a child console process (ffmpeg.exe for that matter) and support clean CTRL-C handling in my process and in the child process (ffmpreg exits normally when CTRL-C is pressed which is a nice feature I wanted to keep working)
None of the solution I found here were working, so I just interop'd Windows' CreateProcess function and it just works w/o any effort, CTRL-C is automatically received by the child app and by the parent app, input and output streams are shared, etc. I was not able to reproduce that kind of code using the standard .NET Process class:
尝试实际发送组合键 Ctrl+C,而不是直接终止进程:
在 MSDN 上查找,您应该在那里找到发送 Ctrl+组合键所需的内容...
我知道您发送 Alt+Key 所需的消息是 WM_SYSTEMKEYDOWN 和 WM_SYSTEMKEYUP,无法告诉您有关 Ctrl 的信息...
Try actually sending the Key Combination Ctrl+C, instead of directly terminating the process:
Look it up on the MSDN, you should find what you need there in order to send the Ctrl+Key combination...
I know that the message you need for sending Alt+Key is WM_SYSTEMKEYDOWN and WM_SYSTEMKEYUP, can't tell you about Ctrl...