> Unfortunately, taskkill is not available widely enough.  E.g., on my
> XP Home edition, it is not available, and I believe it is not there on
> systems older than XP, which we still support.

I am aware that 'taskkill' is not present on windowses (is that a word?) older
than XP. This makes it no worse than 'CreateToolhelp32Snapshot'. But I din't
know of its absence from home editions. My first thought was to seek inspiration
in the Wine project:
  (https://github.com/mirrors/wine/blob/master/programs/taskkill/taskkill.c)
... only to find:
  WINE_FIXME("/T not supported\n");
... so this seems like a dead end.

> This might be "good enough" -- we err on the safe side, and only leave
> some subprocesses not killed in rare situations.  Does this strategy
> solve the problem which started this bug report?  If so, please tell
> the main ideas of how you intend to avoid killing unrelated processes.

Here is some pseudo-code for it...

# This returns [a subset] of the edges in the process tree
def get-process-tree:
  1. let process-snapshot be the current process snapshot
  2. let process-tree be an empty list
  3. for parent-pid, child-pid in process-snapshot:
       if process-start-time(child-pid) < process-start-time(parent-pid):
         add(process-tree, (parent-pid . child-pid))

def kill-process-tree(root-process):
  1. open a process handle to the root-process (I am guessing that Emacs already
     keeps a handle to all process it spawned so this step might be unnecessary)
  2. let initial-process-tree be the return value of get-process-tree
  3. let inital-kill-list be the edges from initial-process-tree that are
     reachable from the root-process
  4. open process handles to every child-pid from inital-kill-list
  5. let revised-process-tree be the return value of get-process-tree
  6. let revised-kill-list be the intersection of inital-kill-list and
     revised-process-tree
  7. kill and close handles to child processes in final-kill-list
  8. close any remaining handles from step (4.)

There are a few non-obvious things here that need a skeptical reading.

The most arcane is that step (6.) in kill-process-tree really returns what we
need to kill.

Other potential issue is the non-atomicity of step (4.) and the possibility of
grabbing a handle to a process that wasn't in initial-process-tree. I claim that
this is not an issue, because those will not end up in revised-kill-list.

Both, of course, I cannot prove formally. But so far I have been unable to find
counterexamples.