Kev*_*lar 28 c# command-line process .net-2.0
我正在为命令行可执行文件编写一个包装类.这个exe接受来自stdin的输入,直到我在命令提示符shell中命中ctrl + c,在这种情况下,它根据输入到stdout打印输出.我想模拟ctrl + c按c#代码,将kill命令发送到.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();
Run Code Online (Sandbox Code Playgroud)
但是,当我手动运行exe时,输出总是为空,即使我从stdout获取数据.编辑:这是c#2.0顺便说一句
Kev*_*lar 30
我其实只是想出了答案.谢谢你们的答案,但事实证明我所要做的就是:
p.StandardInput.Close()
Run Code Online (Sandbox Code Playgroud)
这导致我产生的程序完成从标准输入读取并输出我需要的东西.
Vit*_*nko 29
尽管使用GenerateConsoleCtrlEvent发送Ctrl + C信号是一个正确的答案,但它需要明确的澄清才能使它在不同的.NET应用程序类型中工作.
如果您的.NET应用程序不使用自己的控制台(WinForms/WPF/Windows服务/ ASP.NET),则基本流程为:
以下代码段说明了如何执行此操作:
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;
}
Run Code Online (Sandbox Code Playgroud)
其中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);
Run Code Online (Sandbox Code Playgroud)
如果您需要从.NET控制台应用程序发送Ctrl + C,事情会变得更加复杂.方法不起作用,因为在这种情况下AttachConsole返回false(主控制台应用程序已经有一个控制台).可以在AttachConsole调用之前调用FreeConsole,但结果是原始.NET应用控制台将丢失,这在大多数情况下是不可接受的.
我对这种情况的解决方案(这确实有效,并且对.NET主进程控制台没有副作用):
Rob*_*Rob 21
@alonl:用户正在尝试包装命令行程序.命令行程序没有消息泵,除非它们是专门创建的,即使是这种情况,Ctrl + C在Windows环境应用程序中没有相同的语义(默认情况下为copy),因为它在命令行环境(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();
}
Run Code Online (Sandbox Code Playgroud)
我的输出似乎做你想要的:
4080 is active: True
4080 is active: False
Run Code Online (Sandbox Code Playgroud)
希望有所帮助!
(澄清一下:\ x3是十六进制字符3的十六进制转义序列,它是ctrl + c.它不仅仅是一个幻数.;))
小智 8
好的,这是一个解决方案.
发送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;
}
Run Code Online (Sandbox Code Playgroud)