Stopping command-line applications programatically with Ctrl-C event from .Net – a working demo

The story behind

The problem of starting and stopping console/command-line applications from a host program is much more widespread, than one would think at the first glance. Starting an application and even redirecting its output is relatively easy to achieve. Stopping it nicely, on the other hand, is hard. “Nicely” is the keyword here. One can always call Kill() member function of a Process object. Yet that would terminate the child process immediately, without giving it a chance to do any clean-up, which in some cases can be disastrous.

On a Unix environment, a programmer would typically issue a SIGINT and/or SIGTERM signal, before resorting to SIGKILL. Windows has something almost, but not entirely, like this. It can generate a ConsoleControlEvent, which translates from a Ctrl-C or Ctrl-Break key press. These events are sent not to an application, but to the console/terminal with witch an application is registered and then propagated to all handlers in each application until one of the applications acknowledges the event. So, in reality, one sends a termination signal to all application, which are started from/with a particular console, and not just to a specific application. This, too, can spell trouble.

A popular work-around, which almost never works is to locate the window handle of a process (this assumes that the command line process has a window, which is not always the case) and sending a series of messages, to it, simulating key-down and key-up events, mapping to the ‘Ctrl’ and to the ‘C’ keys on the keyboard.

I was aiming for this approach, until I stumbled across a more elegant, and seemingly more fool-proof solution. Each solution will be outlined and briefly explained in the list below. Download and study the source code for the actual implementation (all worker functions are concentrated in a static class in Experiments.cs). If you happen to have a working solution for the PostMessage scenario, please share it with the community though the comments in this post.

Source code: [download]

Outline of the scenarios

If the started application (ping.exe) fails to stop (mainly as a result of your own experiments or of Scenario 2), hit the yellow Panic button to forcefully terminate it.
Only two scenarios represent real solutions to the problem – 1 and 4, with #4 being preferred. Scenario #2 was never completed due to time constraints (feel free to fill in the blanks ;)), while Scenario #3 is there for the sake of completeness.

  1. Start with a visible window using .Net, hide with pinvoke, run for 6 seconds, show with pinvoke, stop with .Net.

    This method is a mix of .Net and pinvoke. It stops the process by closing its command window, similar to clicking the ‘X’ button in the upper right corner. This generates a Ctrl-C event to all programs, registered with that console. To find the window, it must exist and be visible. Here I use method FindWindowHandleFromProcessObjectWithVisibleWindow() in the Experiments class, which uses .Net functionality. Alternatively I could have used a pinvoke method, which makes use of EnumWindows. This is implemented in FindWindowHandleFromPid() in the Experiments class.
    The downside of this method is that it briefly shows the commend window when starting and when stopping the process. Another problem is that a process takes long to close, it will be terminated. This method works fine for ping, but for my needs it was terminating the process to quickly.

  2. Pinvoke only: Start with invisible window, get window handle, run for 6 seconds, stop using window handle. (Unfinished!)

    It turned out that it was impossible to start a command window with an invisible, but still existing window using pinvoke CreateProcess. The start sequence ended up being the same as for Scenario 1, but bypassing .Net. Stopping a process with PostMessage did not work out either, even though I read of successful attempts. I abandoned this scenario as I did not want to use an inordinate amount of time on what started to look like a dead end.

  3. Brutal .Net: Start without window; run for 6 seconds; stop by killing the process.

    This scenario is is here for completeness. This is the most brutal method, which relies entirely on .Net functionality. When stopping the process, it simply kills it, without giving it any chance to clean up. Note that ping command does not display any summary when it is stopped in this fashion.

  4. Start without a window using .Net, run for 6 seconds, stop by attaching console and issuing ConsoleCtrlEvent.

    After finding a hint at the bottom of this thread, I finally bit the bullet and started the process without a window. When the time comes to stop a process, the parent attaches its console to the child, stops itself listening to Ctrl-C event and issues the event to the console, so that the attached child terminates. It then reinstates the Ctrl-C handler for itself and frees the console.

    UPDATE: Please read comment by Michal Svoboda below for an improved version for this scenario.


During the research phase, I got ideas and inspiration from the following posts and sites. Some them were dead ends, while others lead to something usable. The post in particular (the first link) was a real revelation, and is incorporated in the fourth scenario in the demo application.