Subject: Re: sub-processes and sending Ctrl-C vs. Ctrl-Break

From: Andrew Innes <[email-masked]>
Date: Thu, 8 May 1997 14:58:08 +0100 (BST)

On Thu, 08 May 1997 02:40:00 -0400, Todd Sabin <[email-masked]> said:
>So I looked into this a little today; specifically trying to figure out
>why Ctrl-Break gets through, but Ctrl-C doesn't.  I tested by creating
>a subshell (MKS sh.exe), and running cat.exe (also MKS) with no
>parameters.  cat tried to read from stdin, and just waited.  I then
>sent C-c C-c.  Here's what I was able to determine:
>
>1.  In general the calling sequence goes something like this:
>   o Emacs calls GenerateConsoleCtrlEvent ()
>   o this goes through to CsrSS and is dispatched to a routine named 
>     SrvGenerateConsoleCtrlEvent (in winsrv.dll)
>   o some more stuff happens here which determines which processes
>     are to receive the event
>   o for each process that should receive the event
>       o a thread is created in the target process which eventually
>         makes its way to a routine named CtrlRoutine (in kernel32)
>       o CtrlRoutine does the work of walking the CtrlHandlerList
>         and invoking the registered handlers.
>       o the created thread exits
>
>2.  When sending a Ctrl-Break, everything happens as described above and 
>  all the child processes get the Event.
>
>3.  When sending a Ctrl-C, everything happens _almost_ as described above,
>  except that when the processing reaches CtrlRoutine, a check is made
>  whether the event is a Ctrl-C [actually the check is always made;
>  it's just true now], and if it is, then a test is made against a flag
>  in the process's parameters memory block [see below].  If this flag
>  is set, then the event is just ignored and CtrlRoutine returns
>  without running down the CtrlHandlerList.  If the flag is not set, it
>  proceeds as with Ctrl-Break.
>
>4.  What I'm calling the process parameter memory block is the block of
>  memory starting at 0x20000.  Of course, the real way the address is found
>  is with
>      mov eax, dword ptr fs:[00000018h]  ; get the teb
>      mov eax, dword ptr [eax+30h]       ; get the peb
>      mov eax, dword ptr [eax+10h]       ; get the thing I'm talking about
>  but it's always at 0x20000, as fas as I can tell.  Anyway, the flag
>  is a dword at offset 14h (or 0x20014).  If this flag is 0, then the Ctrl-C
>  will get through, otherwise it won't.
>
>5.  The flag at 0x20014 gets set during CreateProcess based on whether the 
>  CREATE_NEW_PROCESS_GROUP flag was passed.  I.e., if the flag was
>  passed, then the flag is set to one.  If the flag is not set, then
>  the 0x20014 flag _might_ be inherited by subprocesses; I'm not sure
>  about this.  I do know that my MKS sh.exe had its flag set, and it
>  was creating subprocesses which also had it set, but I don't know if
>  sh.exe was passing the CREATE_NEW_PROCESS_GROUP flag or not when it
>  was calling CreateProcess.

This analysis corresponds to the documentation of CreateProcess and
SetConsoleCtrlHandler.  That is, the "Ignore_Ctrl_C" flag (as set by
SetConsoleCtrlHandler) is inherited by child processes, unless
CREATE_NEW_PROCESS_GROUP is specified in which case the flag is forced
on in the child process.

In the release version of 19.34, Emacs uses the CREATE_NEW_PROCESS_GROUP
flag, and consequently sends a CTRL_BREAK_EVENT instead of CTRL_C_EVENT
to fake SIGINT.  This normally works under NT, but has no effect under
Windows 95 (at least when using command.com as the shell program).

In the updated version of 19.34 on the ftp site (known informally as
19.34.2), I changed things so that Emacs now uses the CREATE_NEW_CONSOLE
flag instead of CREATE_NEW_PROCESS_GROUP.  Emacs leaves its own
Ignore_Ctrl_C flag turned off (it decides at runtime whether to ignore
such events) so child processes will by default receive Ctrl-C events as
well.

To fake SIGINT, Emacs now has to find the window handle of the console
allocated for the child process and use keybd_event to emulate the
effect of the user actually typing Ctrl-C.  This works on both NT and
Windows 95.

To fake SIGKILL, Emacs now posts a WM_CLOSE message to the console
window, which on NT causes CTRL_CLOSE_EVENT to be sent to all
applications sharing that console, resulting in a clean orderly shutdown
of the whole process group.  This is an improvement on the original
method of calling TerminateProcess on the child (which doesn't kill
grandchildren and doesn't allow the child to shutdown cleanly).

Unfortunately Windows 95 doesn't fully implement the CTRL_CLOSE_EVENT
handling for console programs (instead it provides a completely
different mechanism for DOS programs which roughly corresponds to this),
so on Win95 the faked SIGKILL is effectively ignored.  I've done some
testing though, and it looks like it will be possible to arrange for
WM_CLOSE to cause a reasonably orderly shutdown by using helper programs
to load a DOS TSR program in the child's VM.

AndrewI