Kill support on Win32
From:
"Berger, Daniel" <djberge@...>
Date:
2003-05-14 20:57:10 UTC
List:
ruby-core #1036
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