views:

1235

answers:

2

I'm having the hardest time trying to get this to work, hoping one of you has done this before.

I have a C# console app that is running a child process which inherits its console. I want a ctrl-c caught by the outer app to be passed along to the inner app so that it can have a chance to shut down nicely.

I have some very simple code. I start a Process, then poll it with WaitForExit(10). I also have a CancelKeyPress handler registered, which sets a bool to true when it fires. The polling loop also checks this, and when it's true, it calls GenerateConsoleCtrlEvent() (which I have mapped through pinvoke).

I've tried a lot of combinations of params to GenerateConsoleCtrlEvent(). 0 or 1 for the first param, and either 0 or the child process's ID for the second param. Nothing seems to work. Sometimes I get a false back and Marshal.GetLastWin32Error() returns 0, and sometimes I get true back. But none cause the child app to receive a ctrl-c.

To be absolutely sure, I wrote a test C# app to be the child app which prints out what's going on with it and verified that manually typing ctrl-c when it runs does properly cause it to quit.

I've been banging my head against this for a couple hours. Can anyone give me some pointers on where to go with this?

+1  A: 

Not so sure this is a good approach. This only works if the child process is created with the CREATE_NEW_PROCESS_GROUP flag for CreateProcess(). The System.Diagnostics.Process class doesn't support this. Consider using the return value from the Main() method. There is already a unique value defined in the Windows SDK for Ctrl+C aborts, STATUS_CONTROL_C_EXIT or 0xC000013A. The parent process can get that return code with Process.ExitCode.

Hans Passant
The trick is - I need a way to signal the child process to abort. Ctrl-c is the standard way to do this with console apps. Not all of the subprocesses we're running are under our control (such as cl.exe and gcc.exe). Thanks for the info about CREATE_NEW_PROCESS_GROUP. I'll try some hacks.
Scott Bilas
Yikes. Reflector tells me that Process.Start() is very complex. Would have to clone it just to set that single flag. Too much work at this time.
Scott Bilas
+2  A: 

Did you have any luck with this? My understanding is that when you press CTRL+C in a console, by default all the processes attached to the console receive it, not just the parent one. Here's an example:

Child.cs:

using System;

public class MyClass
{
    public static void CtrlCHandler(object sender, ConsoleCancelEventArgs args)
    {
        Console.WriteLine("Child killed by CTRL+C.");
    }
    public static void Main()
    {
        Console.WriteLine("Child start.");
        Console.CancelKeyPress += CtrlCHandler;
        System.Threading.Thread.Sleep(4000);
        Console.WriteLine("Child finish.");
    }
}

Parent.cs:

using System;

public class MyClass
{
    public static void CtrlCHandler(object sender, ConsoleCancelEventArgs args)
    {
        Console.WriteLine("Parent killed by CTRL+C.");
    }
    public static void Main()
    {
        Console.CancelKeyPress += CtrlCHandler;
        Console.WriteLine("Parent start.");
        System.Diagnostics.Process child = new System.Diagnostics.Process();
        child.StartInfo.UseShellExecute = false;
        child.StartInfo.FileName = "child.exe";
        child.Start();
        child.WaitForExit();
        Console.WriteLine("Parent finish.");
    }
}

Output:

Y:\>parent
Parent start.
Child start.
Parent killed by CTRL+C.
Child killed by CTRL+C.
^C
Y:\>parent
Parent start.
Child start.
Child finish.
Parent finish.

So I wouldn't have thought you'd need to do anything special. However, if you really need to generate CTRL+C events yourself, things might not be so easy. I'm not sure about the problems you describe, but as far as I can tell you can only send CTRL+C events to all the processes attached to a console window. If you detach a process, you can't send it CTRL+C events. If you want to be selective in which processes to send the CTRL+C events, you seem to need to create new console windows for every one. I've no idea if there's some way to do it without visible windows or when you want to redirect I/O using pipes.

Weeble
I haven't worked on this since - hasn't been a problem lately. But if I remember the problem right, I wanted to be able to tell a child process to abort, and for a lot of command-line apps that means sending a ctrl-c, or signal, or whatever it's called. GenerateConsoleCtrlEvent() is apparently the function to call but it wasn't working. It sounds like CREATE_NEW_PROCESS_GROUP is what I need to set, but the Process class does not support that.
Scott Bilas