0

Somewhere in my application, I am creating a process like this:

STARTUPINFO startupInfo;
ZeroMemory(&startupInfo, sizeof(startupInfo));
startupInfo.cb = sizeof(STARTUPINFO);

PROCESS_INFORMATION processInfo;
ZeroMemory(&processInfo, sizeof(processInfo));

const auto created = CreateProcessW(
                        pathToExe,
                        cmdLine,
                        nullptr,
                        nullptr,
                        false,
                        CREATE_NEW_CONSOLE,
                        nullptr,
                        workingDir,
                        &startupInfo,
                        &processInfo);

The exe I am running here is a console application.

At some later time, I would like to gracefully stop that process. I can kill it with TerminateProcess, but I would like to make it more graceful, as the process might have to save data to some database or perform some clean up (e.g. close hardware connections).

I tried to get the process HWND without success with EnumWindows and GetWindowThreadProcessId. In fact, EnumWindows doesn't list the process I created. With that HWND, my idea was to send a WM_CLOSE message.

Maybe I should not use the CREATE_NEW_CONSOLE flag? I tried the following startup info:

STARTUPINFO startupInfo;
ZeroMemory(&startupInfo, sizeof(startupInfo));
startupInfo.cb = sizeof(STARTUPINFO);
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_SHOW;

but that does not show me a console window (which I need to have). What am I doing wrong? How should I create my process and how can I then later gracefully stop it?

Laurent Michel
  • 1,069
  • 3
  • 14
  • 29
  • Do you have the source code for the child process and can you modify it ? – Richard Critten Nov 15 '21 at 13:57
  • Is the console app expecting a certain key combination to tell it to quit? [How to send a keystroke to an other process (ex a notepad)?](https://stackoverflow.com/a/12100438) – 001 Nov 15 '21 at 14:02
  • 2
    You cannot gracefully terminate a process from the outside. Graceful termination requires coöperation. Unlike GUI applications that commonly respond to and handle `WM_CLOSE` requests, processes targeting the console subsystem do not follow a common scheme that would allow sending termination requests from the outside. – IInspectable Nov 15 '21 at 15:02
  • 2
    https://learn.microsoft.com/en-us/windows/console/generateconsolectrlevent – Hans Passant Nov 15 '21 at 15:23
  • @HansPassant Does CREATE_NEW_PROCESS_GROUP + a non-zero process group you are not a part of actually work though? – Anders Nov 15 '21 at 18:21
  • 1
    EnumWindows is the wrong approach, the terminal/console window is owned by csrss (old NT) or conhost.exe, it is not owned by any of the processes connected to the console. – Anders Nov 15 '21 at 18:38
  • you can call `CreateRemoteThread` for target process with entry point `CtrlRoutine` and `CTRL_C_EVENT` as parameter - https://stackoverflow.com/a/64840517/6401656 – RbMm Nov 15 '21 at 19:21
  • @HansPassant I tried the approach with GenerateConsoleCtrlEvent; the problem is that it sends the CTRL+C signal to the application starting the process, therefore killing the application itself, not only the process. I tried to call AttachConsole on the processId of my process (with the idea to then sending the CTRL+C signal there) without success because my application is already a console application and we can attach only to one console at a time. – Laurent Michel Nov 16 '21 at 05:21
  • @RichardCritten I do have the source code for the child process and I can modify it. I will talk to my colleagues and see if we want to make it accept a certain key combination to tell it to quit. – Laurent Michel Nov 16 '21 at 05:27
  • However, I don't think your suggestion will work @JohnnyMopp, because it requires an HWND to my process, which I cannot have. – Laurent Michel Nov 16 '21 at 05:27
  • @IInspectable how can I achieve cooperation? – Laurent Michel Nov 16 '21 at 05:28
  • Currently my best option seems to be to modify the child process and my application in such a way that they call the `RegisterWindowMessage` function, then my application should call the `BroadcastSystemMessage` function, as explained [here](https://learn.microsoft.com/en-us/windows/win32/procthread/terminating-a-process), finally the process should call `ExitProcess` upon message reception. I was not able to find anything simpler. Is there? – Laurent Michel Nov 16 '21 at 05:46
  • As you can modify the child process you can use one of the inter-process communication mechanisms to ask it to close down gracefully. Some examples of IPC are - (a) parent creates a file with a known name and location; child pols for this file and when it see it, deletes it and closes down; (b) signalling a Win32API [Semaphore](https://learn.microsoft.com/en-us/windows/win32/sync/semaphore-objects) again when signalled the child will closes down gracefully; (c) UPD; (d) TCP/IP; (e) shared memory. You can't use the easiest method - a MS-Windows message - as the child is a console app. – Richard Critten Nov 16 '21 at 09:32
  • @RichardCritten "*You can't use the easiest method - a MS-Windows message - as the child is a console app*" - not true. A Windows console app has full access to the Win32 API and can simply create its own hidden window via `CreateWindow/Ex()` for IPC purposes. – Remy Lebeau Nov 16 '21 at 23:55
  • @RemyLebeau was trying to keep it simple. If you try to process messages then you need a message loop, Which means the original processing probably needs to move to a background thread (or poll messages at multiple places), now you have to close the worker thread down cleanly. – Richard Critten Nov 17 '21 at 07:50

1 Answers1

0

In fact, at the time where I was trying to gracefully kill my console, it was too early to get a HWND to the console window. It was in a test where I naively just created the process and then almost straightaway killed it. If I wait long enough (i.e. if I actually interact for some time with the process I created), then I can get the handle to the console window and send it a WM_CLOSE message with success.

Laurent Michel
  • 1,069
  • 3
  • 14
  • 29