Method: Process.kill
- Defined in:
- lib/win32/process.rb
.kill(signal, *pids) ⇒ Object
Kill a given process with a specific signal. This overrides the default implementation of Process.kill. The differences mainly reside in the way it kills processes, but this version also gives you finer control over behavior.
Internally, signals 2 and 3 will generate a console control event, using a ctrl-c or ctrl-break event, respectively. Signal 9 terminates the process harshly, given that process no chance to do any internal cleanup. Signals 1 and 4-8 kill the process more nicely, giving the process a chance to do internal cleanup before being killed. Signal 0 behaves the same as the default implementation.
When using signals 1 or 4-8 you may specify additional options that allow finer control over how that process is killed and how your program behaves.
Possible options for signals 1 and 4-8.
:exit_proc => The name of the exit function called when signal 1 or 4-8
is used. The default is 'ExitProcess'.
:dll_module => The name of the .dll (or .exe) that contains :exit_proc.
The default is 'kernel32'.
:wait_time => The time, in milliseconds, to wait for the process to
actually die. The default is 5ms. If you specify 0 here
then the process does not wait if the process is not
signaled and instead returns immediately. Alternatively,
you may specify Process::INFINITE, and your code will
block until the process is actually signaled.
Example:
Process.kill(1, 12345, :exit_proc => 'ExitProcess', :module => 'kernel32')
747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 |
# File 'lib/win32/process.rb', line 747 def kill(signal, *pids) # Match the spec, require at least 2 arguments if pids.length == 0 raise ArgumentError, "wrong number of arguments (1 for at least 2)" end # Match the spec, signal may not be less than zero if numeric if signal.is_a?(Numeric) && signal < 0 # EINVAL raise SystemCallError.new(22) end # Match the spec, signal must be a numeric, string or symbol unless signal.is_a?(String) || signal.is_a?(Numeric) || signal.is_a?(Symbol) raise ArgumentError, "bad signal type #{signal.class}" end # Match the spec, making an exception for BRK/SIGBRK, if the signal name is invalid. # Older versions of JRuby did not include KILL, so we've made an explicit exception # for that here, too. if signal.is_a?(String) || signal.is_a?(Symbol) signal = signal.to_s.sub("SIG", "") unless Signal.list.keys.include?(signal) || %w{KILL BRK}.include?(signal) raise ArgumentError, "unsupported name '#{signal}'" end end # If the last argument is a hash, pop it and assume it's a hash of options if pids.last.is_a?(Hash) hash = pids.pop opts = {} valid = %w{exit_proc dll_module wait_time} hash.each { |k, v| k = k.to_s.downcase unless valid.include?(k) raise ArgumentError, "invalid option '#{k}'" end opts[k] = v } exit_proc = opts["exit_proc"] || "ExitProcess" dll_module = opts["dll_module"] || "kernel32" wait_time = opts["wait_time"] || 5 else wait_time = 5 exit_proc = "ExitProcess" dll_module = "kernel32" end count = 0 pids.each { |pid| raise TypeError unless pid.is_a?(Numeric) # Match spec, pid must be a number raise SystemCallError.new(22) if pid < 0 # Match spec, EINVAL if pid less than zero sigint = [Signal.list["INT"], "INT", "SIGINT", :INT, :SIGINT, 2] # Match the spec if pid == 0 && !sigint.include?(signal) raise SystemCallError.new(22) end if signal == 0 access = PROCESS_QUERY_INFORMATION | PROCESS_VM_READ elsif signal == 9 access = PROCESS_TERMINATE else access = PROCESS_ALL_ACCESS end begin handle = OpenProcess(access, 0, pid) if signal != 0 && handle == 0 raise SystemCallError, FFI.errno, "OpenProcess" end case signal when 0 if handle != 0 count += 1 else if FFI.errno == ERROR_ACCESS_DENIED count += 1 else raise SystemCallError.new(3) # ESRCH end end when Signal.list["INT"], "INT", "SIGINT", :INT, :SIGINT, 2 if GenerateConsoleCtrlEvent(CTRL_C_EVENT, pid) count += 1 else raise SystemCallError.new("GenerateConsoleCtrlEvent", FFI.errno) end when Signal.list["BRK"], "BRK", "SIGBRK", :BRK, :SIGBRK, 3 if GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, pid) count += 1 else raise SystemCallError.new("GenerateConsoleCtrlEvent", FFI.errno) end when Signal.list["KILL"], "KILL", "SIGKILL", :KILL, :SIGKILL, 9 if TerminateProcess(handle, pid) count += 1 else raise SystemCallError.new("TerminateProcess", FFI.errno) end else thread_id = FFI::MemoryPointer.new(:ulong) mod = GetModuleHandle(dll_module) if mod == 0 raise SystemCallError.new("GetModuleHandle: '#{dll_module}'", FFI.errno) end proc_addr = GetProcAddress(mod, exit_proc) if proc_addr == 0 raise SystemCallError.new("GetProcAddress: '#{exit_proc}'", FFI.errno) end thread = CreateRemoteThread(handle, nil, 0, proc_addr, nil, 0, thread_id) if thread > 0 WaitForSingleObject(thread, wait_time) count += 1 else raise SystemCallError.new("CreateRemoteThread", FFI.errno) end end ensure CloseHandle(handle) if handle end } count end |