Eryk Sun <[email protected]> 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 <[email protected]>
<https://bugs.python.org/issue33245>
_______________________________________
_______________________________________________
Python-bugs-list mailing list
Unsubscribe:
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com