none
How to send CTRL+C signal to detached command-line process?

    Question

  • Here's the problem: my company has a lot of command-line programs that do fairly heavy amounts of processing. As part of their design they handle the SIGINT (Ctrl+C) signal, which the user can use to inform them to shut down gently.

    My group is writing a GUI application to wrap up these command-line programs. The GUI program spawns the cmd-line programs using 'CreateProcess'. The child processes all run detached (CREATE_NO_WINDOW). I have handles to the child processes and I need to be able to send them SIGINT when the user hits a "Cancel" button in the UI.

    You'd think GenerateConsoleCtrlEvent would be what I needed, but according to the Remarks for this method, it explicitly fails if you attempt to pass in CTRL_C_EVENT as an argument. [edit: I should say, fails to deliver the SIGINT when targeted at a particular process group].

    Any thoughts on how I can send a Ctrl+C signal to a detached command-line child process? I'm primarily targeting Windows XP.

    for search indexing: CTRL+C, CTRL C, Control C, SIGINT.
    Wednesday, February 04, 2009 3:59 PM

Answers

  • One of my coworkers came up with a good solution, which actually does pivot on GenerateConsoleCtrlEvent after all. The missing piece is a helper program that sits in between the main program and the interruptable child program you want to spawn. This program is responsible for creating the child process via CreateProcess, and then listening for some IPC signal from the main process. When it receives that signal, it kills both itself and its child via GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0 );

    We're using the event system, so the core of our spawn helper program looks like this:

    HANDLE events[2];
    events[0] = cancelEvent;
    events[1] = processInfo.hProcess;
    
    
     switch ( WaitForMultipleObjects( 2, events, FALSE, INFINITE ) )
        {
          case WAIT_OBJECT_0 + 0:
            // We recieved a cancel event, KAMIKAZE!
            //
            // Either Ctrl+C or Ctrl+Break events would work here
    GenerateConsoleCtrlEvent( CTRL_C_EVENT, 0 ); WaitForSingleObject( processInfo.hProcess, INFINITE ); break; case WAIT_OBJECT_0 + 1: GetExitCodeProcess( processInfo.hProcess, &exitCode ); break; }
    Once the helper has spawned the child process, it waits to hear EITHER that the process has gone away on its own, or that the
    Public Event it instantiated earlier has been signalled. If the latter, it generates the Ctrl+C signal and waits indefinitely
    for the process to finish exiting.
    Worth noting that Visual Studio needs to do the exact same indirection with VCBuildHelper.exe when spawning CL.exe.
    Wednesday, June 03, 2009 3:24 PM

All replies

  • Are you sure? My reading of GenerateConsoleCtrlEvent is that it only fails if SetConsoleCtrlHandler has deliberately enabled the attribute to ignore CTRL+C events.
    Wednesday, February 04, 2009 4:54 PM
  • pretty sure, yeah. The problem is that I can't target a specific process with Ctrl+C using that command, because:

    "This signal [Ctl+C] cannot be generated for process groups. If dwProcessGroupId is nonzero, this function will succeed, but the CTRL+C signal will not be received by processes within the specified process group."

    The only alternative is passing in 0 as the Process Group ID, which Ctrl+C's all processes sharing the console window. But there isn't a console window; it's a GUI program spawning detached child programs. Also, even if that did work, it would probably kill the parent process too.

    Thanks for the reply though.
    Wednesday, February 04, 2009 9:35 PM
  • Hi David, did you ever solve this one? I am trying to achieve the same exact thing. Thanks.
    Sunday, May 31, 2009 12:23 PM
  • One of my coworkers came up with a good solution, which actually does pivot on GenerateConsoleCtrlEvent after all. The missing piece is a helper program that sits in between the main program and the interruptable child program you want to spawn. This program is responsible for creating the child process via CreateProcess, and then listening for some IPC signal from the main process. When it receives that signal, it kills both itself and its child via GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0 );

    We're using the event system, so the core of our spawn helper program looks like this:

    HANDLE events[2];
    events[0] = cancelEvent;
    events[1] = processInfo.hProcess;
    
    
     switch ( WaitForMultipleObjects( 2, events, FALSE, INFINITE ) )
        {
          case WAIT_OBJECT_0 + 0:
            // We recieved a cancel event, KAMIKAZE!
            //
            // Either Ctrl+C or Ctrl+Break events would work here
    GenerateConsoleCtrlEvent( CTRL_C_EVENT, 0 ); WaitForSingleObject( processInfo.hProcess, INFINITE ); break; case WAIT_OBJECT_0 + 1: GetExitCodeProcess( processInfo.hProcess, &exitCode ); break; }
    Once the helper has spawned the child process, it waits to hear EITHER that the process has gone away on its own, or that the
    Public Event it instantiated earlier has been signalled. If the latter, it generates the Ctrl+C signal and waits indefinitely
    for the process to finish exiting.
    Worth noting that Visual Studio needs to do the exact same indirection with VCBuildHelper.exe when spawning CL.exe.
    Wednesday, June 03, 2009 3:24 PM