如何发送ctrl + c到c#中的进程?
我正在为命令行可执行文件编写包装类。这个exe接受来自stdin的输入,直到我在命令提示符shell中按ctrl + c,在这种情况下,它会根据stdout的输入打印输出。我想模拟ctrl + c在c#代码中按下,发送kill命令给.Net过程对象。我曾尝试调用Process.kill(),但似乎并没有在流程的StandardOutput StreamReader中给我任何东西。可能有什么我不正确的做法?下面是我尝试使用代码:如何发送ctrl + c到c#中的进程?
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();
但输出始终是空的,即使我得到的数据,从stdout中回来时,我手动运行exe。 编辑:这是C#2.0 BTW
我其实只是想出了答案。谢谢大家既为你的答案,但事实证明,我必须做的是这样的:
p.StandardInput.Close()
导致我产生了要完成从标准输入和输出读什么我需要的程序。
尝试实际发送组合键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,你会发现你需要有什么为了发送Ctrl +组合键... 我知道你需要发送Alt + Key的消息是WM_SYSTEMKEYDOWN和WM_SYSTEMKEYUP,不能告诉你有关Ctrl ...
如果该过程在没有窗口的情况下开始隐藏? – FindOutIslamNow 2017-11-28 12:21:20
@alonl:用户是试图包装一个命令行程序。命令行程序没有消息泵,除非它们是专门创建的,即使是这种情况,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 is active: True 4080 is active: False
希望帮助!
(澄清:\ X3是十六进制字符3,这是Ctrl + C十六进制转义序列它不仅是一个神奇的数字;))
使用既分配Console.CancelKeyPress委托并执行Console.ReadLine()的测试程序; StandardInput.WriteLine(“\ x3”)的建议解决方案;不会完成ReadLine调用,但不会(对我来说)触发CancelKeyPress委托。错误/错误的正确性演示,因为任何输入,而不仅仅是ctrl + c,会触发一个进程退出? (在键盘上按ctrl + c确实会触发代表) – 2013-01-15 02:40:51
@David Burg:可能是框架代码中的错误修正?这篇文章创作了三个版本,并在4年前出版。 – Rob 2013-01-15 06:17:47
若要澄清(对于gravedig抱歉),如果您没有从stdin读取数据,则这不起作用,因为它实际上并未发送信号 – 2015-06-01 19:26:25
好的,这是一个解决方案。
发送Ctrl-C信号的方法是使用GenerateConsoleCtrlEvent。但是,此调用需要一个processGroupdID参数,并将Ctrl-C信号发送到组中的所有进程。如果不是因为没有办法在.net中产生与您(父)所在的进程组不同的子进程,那就没有问题了。因此,当您发送GenerateConsoleCtrlEvent时,子进程和你(家长)取得联系。所以,你需要捕获父项中的ctrl-c事件,然后确定你是否需要忽略它。
在我的情况下,我希望父母也能够处理Ctrl-C事件,所以我需要在控制台上由用户发送的Ctrl-C事件和父进程发送给儿童。我只是在设置/取消设置布尔标志的同时,将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;
}
尽管事实上,使用GenerateConsoleCtrlEvent发送按Ctrl + C信号是需要显著澄清得到它在不同的工作中正确的答案。 NET应用程序类型。
如果你的.NET应用程序不使用自己的控制台(的WinForms/WPF/Windows服务/ ASP.NET)基本流程是:
- 连接主.NET程序要安慰过程
- 产生当前控制台GenerateConsoleCtrlEvent(processGroupId控制台的事件按Ctrl + C
- 防止主.NET从SetConsoleCtrlHandler因为按Ctrl + C事件停止过程应该是零!与发送p.SessionId代码答案不工作和不正确) 012从控制台
- 断开和恢复按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应用程序控制台将丢失,这在大多数情况下是不可接受的。
我对这种情况下的解决方案(中真正起作用的,并且没有副作用.NET主要工艺控制台):
- 创建命令行参数的接受进程ID小的支持.NET控制台程序,失去其自己的控制台FreeConsole AttachConsole呼叫之前,发送Ctrl + C可针对以上
- 主要.NET控制台进程中提到码进程只是在调用新工艺此实用工具时,它需要按Ctrl + C发送到另一台主机的过程
请注意,只有当进程正在尝试从标准输入读取时,它才起作用。直到程序试图从中读取某些东西,关闭stdin才会执行任何操作。 – Doug 2013-03-12 23:00:50