当前位置:  开发笔记 > 编程语言 > 正文

如何将ctrl + c发送到c#中的进程?

如何解决《如何将ctrl+c发送到c#中的进程?》经验,为你挑选了4个好方法。

我正在为命令行可执行文件编写一个包装类.这个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();

但是,当我手动运行exe时,输出总是为空,即使我从stdout获取数据.编辑:这是c#2.0顺便说一句



1> Kevlar..:

我其实只是想出了答案.谢谢你们的答案,但事实证明我所要做的就是:

p.StandardInput.Close()

这导致我产生的程序完成从标准输入读取并输出我需要的东西.


请注意,它仅在进程尝试从标准输入读取时才有效.在程序尝试从中读取内容之前,关闭stdin不会执行任何操作.

2> Vitaliy Fedo..:

尽管使用GenerateConsoleCtrlEvent发送Ctrl + C信号是一个正确的答案,但它需要明确的澄清才能使它在不同的.NET应用程序类型中工作.

如果您的.NET应用程序不使用自己的控制台(WinForms/WPF/Windows服务/ ASP.NET),则基本流程为:

    将主要.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应用控制台将丢失,这在大多数情况下是不可接受的.

我对这种情况的解决方案(这确实有效,并且对.NET主进程控制台没有副作用):

    创建小的支持.NET控制台程序,从命令行参数接受进程ID,在AttachConsole调用之前用FreeConsole丢失自己的控制台,并使用上面提到的代码将Ctrl + C发送到目标进程

    当需要将Ctrl + C发送到另一个控制台进程时,主.NET控制台进程只在新进程中调用此实用程序



3> Rob..:

@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();
}

我的输出似乎做你想要的:

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确实会触发代表给我)

4> 小智..:

好的,这是一个解决方案.

发送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;
}


`p.SessionId`不是`GenerateConsoleCtrlEvent`的正确参数.
此代码无法正常工作.GenerateConsoleCtrlEvent需要进程组ID,而不是终端会话ID.
推荐阅读
小色米虫_524
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有