0

From my program I need to run an external console program in the background:

p = new Process()
{
    StartInfo = new ProcessStartInfo()
    {
        FileName = "someConsoleApplication.exe",
        Arguments = "someArguments",
        UseShellExecute = false,
        CreateNoWindow = true,
        RedirectStandardError = true,
        RedirectStandardOutput = true,
    }
};
p.Start();

It works. The program runs in the background, does its work and then exits. But in some cases I need to stop the program when it's running. And here's the problem. If I do

p.Kill();

the program exits without saving data. So I need some way to "ask" program to exit, not to kill it. When I run it without "CreateNoWindow", it exits correctly if I close the window or press Ctrl+C. I've tried to send Ctrl+C message directly to the program when it's in the background mode (using Windows API), but it seems that programs can't process such messages when they have no window.

I also have found one not very good way to do so:

[DllImport("kernel32.dll", SetLastError = true)]
static extern bool GenerateConsoleCtrlEvent(ConsoleCtrlEvent sigevent, int dwProcessGroupId);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool AttachConsole(uint dwProcessId);
[DllImport("kernel32.dll")]
static extern bool SetConsoleCtrlHandler(ConsoleCtrlDelegate HandlerRoutine, bool Add);
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
static extern bool FreeConsole();

public enum ConsoleCtrlEvent
{
    CTRL_C = 0,
    CTRL_BREAK = 1,
    CTRL_CLOSE = 2,
    CTRL_LOGOFF = 5,
    CTRL_SHUTDOWN = 6
}

private void CrackingProcessCleanKill()
{
    if (AttachConsole((uint)p.Id))
    {
        SetConsoleCtrlHandler(null, true);
        GenerateConsoleCtrlEvent(ConsoleCtrlEvent.CTRL_C, 0);
        FreeConsole();
        SetConsoleCtrlHandler(null, false);
    }
}

But sometimes it unpredictably causes the whole application to close, not just the process p. It's also not a good solution, because that code can't be used in unit tests (or maybe it's my mistake?). I mean it works when used from a real application and throws exceptions when used from unit test.

So, what's the right way to do what I want?

Mong Zhu
  • 23,309
  • 10
  • 44
  • 76
Igor
  • 600
  • 6
  • 13
  • If you can (.NET Framework 4+) use Tasks. A Task has the capability of being programmatically canceled. See: https://msdn.microsoft.com/en-us/library/dd997396(v=vs.110).aspx – Kevin Jun 27 '16 at 13:11
  • @Igor Kotlerman, Did you try to send WM_CLOSE message ? – Artavazd Balayan Jun 27 '16 at 13:34
  • @Kevin, I can't use Tasks, I need to run external program (not mine). – Igor Jun 27 '16 at 13:38
  • @ArtavazdBalayan, I've tried to send WM_CLOSE (0x0010): SendMessage(p.MainWindowHandle, 0x010, IntPtr.Zero, IntPtr.Zero). But it should be send to a Window, and when I set CreateNoWindow to true, MainWindowHandle from now on returns 0 (because it just have no window) – Igor Jun 27 '16 at 13:41
  • 1
    @IgorKotlerman, sorry, I have not noticed that your external app is console app. As I can see from [this answer](http://stackoverflow.com/a/8699583/5794617), you have used correct solution. But you've said that some times your app closed. Do you get any exceptions or errors? What is the exit code of your app after that scenario ? – Artavazd Balayan Jun 27 '16 at 13:46
  • @ArtavazdBalayan, I don't get any exceptions or errors - it closes as if I click the red cross. But I've just tried to get exit code and have noticed that it only exits if it is ran with Visual Studio's debugger. It works correctly when ran directly. – Igor Jun 27 '16 at 14:43
  • Possibly timing issue, see in depth discussion in https://stackoverflow.com/questions/813086/can-i-send-a-ctrl-c-sigint-to-an-application-on-windows – rogerdpack Jan 29 '19 at 14:37
  • 1
    Possible duplicate of [Can I send a ctrl-C (SIGINT) to an application on Windows?](https://stackoverflow.com/questions/813086/can-i-send-a-ctrl-c-sigint-to-an-application-on-windows) – rogerdpack Jan 29 '19 at 14:40

0 Answers0