Eryk Sun email@example.com added the comment:
Popen.send_signal() documents that sending CTRL_C_EVENT (cancel) to a process group is possible, which is clearly a true statement and easily demonstrated. OTOH, the Windows documentation of GenerateConsoleCtrlEvent() claims it's not possible. If you know what the system really does for this case, then the latter claim reads as inconsistent in spirit with the "remarks" section on the very same page, as well as the documentation of SetConsoleCtrlHandler(). It's also strangely worded to say the event isn't "received" by processes in the group, when it's actually about whether each individual process elects to ignore the cancel event that it receives.
The documentation of send_signal() could explain that the cancel event may be ignored in processes, which is initially the case for a new group and is inheritable by child processes. Sending the cancel event to all processes in the console session (process group 0) does nothing to resolve the general problem. It can help with a particular problem where the application has desired behavior for the cancel event and is known to be neutral about ignoring it, i.e. it never calls SetConsoleCtrlHandler(NULL, ...) to either enable or disable ignoring of the cancel event, and is known to not have been created as a new process group or by a parent process that ignores the cancel event.
It's worth discussing that CTRL_BREAK_EVENT can never be ignored at the process level. An application has to go out of its way to ignore the break event. It's the preferred event to send a console application when you need to terminate the process. Often it calls the default handler, which calls ExitProcess(), which at least gives shared libraries a chance to detach cleanly (i.e. DLL_PROCESS_DETACH). The C runtime maps the break event to SIGBREAK, and it also maps CTRL_CLOSE_EVENT to SIGBREAK. Thus if all you can set is a C signal handler, as is the case for Python scripts by default, then you need to handle SIGBREAK in order to exit gracefully for the cases of closing the console, manual Ctrl+Break, and a generated break event. This includes using Task Manager or taskkill.exe to non-forcefully kill the process that effectively owns the console session, which is implemented by sending WM_CLOSE to the console window.
---------- versions: +Python 3.10, Python 3.9 -Python 2.7, Python 3.4, Python 3.5, Python 3.6