Eryk Sun <eryk...@gmail.com> added the comment: Unless you're willing to step through hoops using ctypes or PyWin32, sending Ctrl+Break requires already being attached to the same console as the target. Note that the target isn't necessarily a single process that's attached to the console. Sending a control event is implemented by calling WinAPI GenerateConsoleCtrlEvent, which targets process group identifiers (i.e. like Unix killpg), not process identifiers (i.e. not like Unix kill).
FYI, to generate the event, the console host (conhost.exe) uses an LPC port to relay the request to the session Windows server (csrss.exe), which is privileged to inject a thread in each target process. This control thread starts executing at a known entry point named "CtrlRoutine" in kernelbase.dll. (Prior to Windows 7 the console is actually hosted in csrss.exe, so there's no need to relay the request, and "CtrlRoutine" is implemented in kernel32.dll instead.) If you don't know the group ID, then the only option is to broadcast Ctrl+Break to group 0, which includes every process that's attached to the console. First ignore SIGBREAK (the C signal for CTRL_BREAK_EVENT) via signal.signal(signal.SIGBREAK, signal.SIG_IGN). Otherwise the default handler will run, which exits the current process. Then send Ctrl+Break via os.kill(0, signal.CTRL_BREAK_EVENT). On the other hand, if you started the process as a new group, then its ID is also the group ID. For example, p = subprocess.Popen('vault.exe', creationflags=subprocess.CREATE_NEW_PROCESS_GROUP). In this case you can send Ctrl+Break via os.kill(p.pid, signal.CTRL_BREAK_EVENT). Windows will generate the event only in the subset of processes that are both in the target process group and currently attached to the console that originated the request. The current implementation of os.kill() is behaving as designed and documented in Python 2.7. There is no bug in this case. However, the docs should clarify that the target when sending a console control event is a process group, not a process. They should also refer to the subprocess docs to explain how to create a new group via the CREATE_NEW_PROCESS_GROUP creation flag. Also, the line about accepting process handles should be stricken. A process handle is an integer that's indistinguishable from a process/group identifier, so the statement makes no sense, and thankfully the code doesn't try to implement anything that crazy. In Python 3, someone decided that if GenerateConsoleCtrlEvent fails, os.kill() should also try TerminateProcess, to allow terminating the process explicitly with exit codes 0 (CTRL_C_EVENT) and 1 (CTRL_BREAK_EVENT). Of course, killing a process using a console control event is nothing at all like terminating it abruptly, so to me this looks quite strange; it's coding with a sledgehammer. Anyway, if TerminateProcess succeeds it should, but currently does not, clear the error from the failed GenerateConsoleCtrlEvent call. That's a bug. I suggest resolving the bug by partially backing out of the change. It should only call GenerateConsoleCtrlEvent for the enum values signal.CTRL_C_EVENT and signal.CTRL_BREAK_EVENT, which I think was suggested by Steve Dower at one point. TerminateProcess can be called otherwise for 0 and 1 values. In this case, os.kill() would no longer magically switch between fundamentally different functions in a single call. An exception should always be raised if GenerateConsoleCtrlEvent fails. Possibly in 3.8, os.killpg() could be added on Windows for sending console control events to process groups, while deprecating or discouraging using os.kill() for this. ---------- nosy: +eryksun versions: +Python 3.7, Python 3.8 _______________________________________ Python tracker <rep...@bugs.python.org> <https://bugs.python.org/issue33245> _______________________________________ _______________________________________________ Python-bugs-list mailing list Unsubscribe: https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com