Hi all,

I would like to see Process.kill support added for Win32.  I've written a
module that does this (sys-win32process), but I would like to see it added
to the core.  Quick synopsis:

 kill 0      -> Test to see if process is running, don't kill
 kill 1, 4-8 -> Nice kill
 kill 2      -> Send Ctrl+Break (console only)
 kill 3      -> Send Ctrl+C (console only)
 kill 9      -> Hard kill

The only problem I have with it now is that it sys_fail(0) returns
Errno::E000 on kill 1-9 (and Errno::ENOENT for kill 0) if the process
doesn't exist.  I would like to force Errno::ESRCH to be consistent with
*nix.  A couple of quick notes:

AFAIK there is no way to tell the difference between a failure attempting to
get info on PID 0 and a process that isn't running.  That's why I assume PID
0 (System Idle Process) is *always* running.

Some processes (notably CSRSS.EXE) will return ERROR_ACCESS_DENIED.  My
opinion is that kill 0 should be considered successful in such a case.

Here's the code I'm using currently:

#include <windows.h>

VALUE cWin32P;
int os_supported;

static VALUE win32p_kill(int argc, VALUE *argv)
{
   int signal;
   int i;
   HANDLE hProcess;
   HANDLE hThread;
   DWORD dwPid;
   DWORD dwThreadId;
   DWORD dwTimeout = 5;
   VALUE killed_pids = rb_ary_new();
   
   if(argc < 2)
   {
      rb_raise(rb_eArgError, "wrong number of arguments -- kill(sig,
pid...)");
   }
   
   if(TYPE(argv[0]) == T_FIXNUM)
   {
      signal = FIX2INT(argv[0]);
   }
   else
   {
      rb_raise(rb_eArgError, "bad signal type");
   }
   
   for(i = 1; i < argc; i++)
   {
       dwPid = (DWORD)FIX2INT(argv[i]);

       /*******************************************************************
       * Attempting to get ALL_ACCESS on system processes will fail, so we 
       * use different access for signal 0, where we simply want to know
       * if the process is running or not.
       *******************************************************************/
       if(signal == 0)
       {
          hProcess = OpenProcess(PROCESS_QUERY_INFORMATION|PROCESS_VM_READ,
                                 FALSE,dwPid);
       }
       else
       {
          hProcess = OpenProcess(PROCESS_ALL_ACCESS,FALSE,dwPid);
       }
               
       switch(signal)
       {
          case 0:
             if(hProcess)
             {
                rb_ary_push(killed_pids,argv[i]);
             }
             else
             {
 
/************************************************************
                * An ACCESS_DENIED error necessarily means that the process
                * is running.  In addition, always assume that PID 0 (System
                * Idle Process) is running.
 
*************************************************************/
                if( (GetLastError == ERROR_ACCESS_DENIED) || (dwPid == 0) )
                {
                   rb_ary_push(killed_pids,argv[i]);
                }
                else
                {
                   rb_sys_fail(0);
                }
             }
             break;
          case 2:
             if(GenerateConsoleCtrlEvent(CTRL_C_EVENT,dwPid))
             {
                rb_ary_push(killed_pids,argv[i]);
             }
             else
             {
                rb_sys_fail(0);
             }
             break;
          case 3:
             if(GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT,dwPid))
             {
                rb_ary_push(killed_pids,argv[i]);
             }
             else
             {
                rb_sys_fail(0);
             }
             break;               
          case 9:
             if(TerminateProcess(hProcess,signal))
             {
                CloseHandle(hProcess);
                rb_ary_push(killed_pids,argv[i]);
             }
             else
             {
                rb_sys_fail(0);
             }
             break;
          default:
             if(hProcess)
             {
                if(os_supported == 1)
                {
                   hThread =
CreateRemoteThread(hProcess,NULL,0,(LPTHREAD_START_ROUTINE)
                       (GetProcAddress(GetModuleHandle("KERNEL32.DLL"),
                       "ExitProcess")),0,0,&dwThreadId);
                    
                   if(hThread)
                   {
                      WaitForSingleObject(hThread, dwTimeout);

                      CloseHandle(hProcess);
                      rb_ary_push(killed_pids,argv[i]);
                   }
                   else
                   {
                      CloseHandle(hProcess);
                      rb_sys_fail(0);
                   }
                }
                else
                {
                   if(TerminateProcess(hProcess,signal))
                   {
                      CloseHandle(hProcess);
                      rb_ary_push(killed_pids,argv[i]);
                   }
                   else
                   {
                      rb_sys_fail(0);
                   }
                }
             }
             else
             {
                rb_sys_fail(0);
             }
             break;         
       }
   }
   return killed_pids;
}

/* Win9x does not support CreateRemoteThread */
int win32p_os_supported()
{
   OSVERSIONINFO OSInfo;
   OSInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
   GetVersionEx(&OSInfo);

   if(OSInfo.dwPlatformId == VER_PLATFORM_WIN32_NT)
   {
      return 1;
   }
   else
   {
      return 0;
   }
}

Regards,

Dan