win32-process 0.6.5 → 0.6.6
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +6 -0
- data/Rakefile +1 -1
- data/lib/win32/process.rb +119 -119
- data/test/test_win32_process.rb +20 -23
- data/win32-process.gemspec +3 -4
- metadata +66 -79
data/CHANGES
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
= 0.6.6 - 19-Jul-2012
|
2
|
+
* Fixed a param bug in the Process.fork method caused by a prototype change
|
3
|
+
in the Windows::Process library of the windows-pr gem. Thanks go to
|
4
|
+
mzverev for the spot.
|
5
|
+
* Updated the gemspec, including updates to the windows-pr dependency.
|
6
|
+
|
1
7
|
= 0.6.5 - 27-Dec-2010
|
2
8
|
* Fixed getpriority and setpriority so that the underlying process handle is
|
3
9
|
always closed. Thanks go to Rafal Michalski for the spot and patch.
|
data/Rakefile
CHANGED
data/lib/win32/process.rb
CHANGED
@@ -19,16 +19,16 @@ module Process
|
|
19
19
|
# Eliminates redefinition warnings.
|
20
20
|
undef_method :getpriority, :kill, :getrlimit, :ppid, :setrlimit
|
21
21
|
undef_method :setpriority, :wait, :wait2, :waitpid, :waitpid2, :uid
|
22
|
-
|
22
|
+
|
23
23
|
# The version of the win32-process library
|
24
|
-
WIN32_PROCESS_VERSION = '0.6.
|
24
|
+
WIN32_PROCESS_VERSION = '0.6.6'
|
25
25
|
|
26
26
|
# Defined for interface compatibility, but not used
|
27
27
|
|
28
28
|
PRIO_PROCESS = 0
|
29
29
|
PRIO_PGRP = 1
|
30
30
|
PRIO_USER = 2
|
31
|
-
|
31
|
+
|
32
32
|
include Windows::Process
|
33
33
|
include Windows::Thread
|
34
34
|
include Windows::Error
|
@@ -53,9 +53,9 @@ module Process
|
|
53
53
|
extend Windows::Unicode
|
54
54
|
extend Windows::ToolHelper
|
55
55
|
extend Windows::MSVCRT::String
|
56
|
-
|
56
|
+
|
57
57
|
# :stopdoc:
|
58
|
-
|
58
|
+
|
59
59
|
# Used by Process.create
|
60
60
|
ProcessInfo = Struct.new("ProcessInfo",
|
61
61
|
:process_handle,
|
@@ -63,7 +63,7 @@ module Process
|
|
63
63
|
:process_id,
|
64
64
|
:thread_id
|
65
65
|
)
|
66
|
-
|
66
|
+
|
67
67
|
@child_pids = [] # Static variable used for Process.fork
|
68
68
|
@i = -1 # Static variable used for Process.fork
|
69
69
|
|
@@ -75,7 +75,7 @@ module Process
|
|
75
75
|
RLIMIT_RSS = 5 # ProcessMemoryLimit
|
76
76
|
RLIMIT_VMEM = 5 # ProcessMemoryLimit
|
77
77
|
end
|
78
|
-
|
78
|
+
|
79
79
|
# :startdoc:
|
80
80
|
|
81
81
|
# Returns the process and system affinity mask for the given +pid+, or the
|
@@ -328,7 +328,7 @@ module Process
|
|
328
328
|
# 256 - Process::REALTIME_PRIORITY_CLASS
|
329
329
|
# 16384 - Process::BELOW_NORMAL_PRIORITY_CLASS
|
330
330
|
# 32768 - Process::ABOVE_NORMAL_PRIORITY_CLASS
|
331
|
-
#
|
331
|
+
#
|
332
332
|
def getpriority(kind, int)
|
333
333
|
raise TypeError unless kind.is_a?(Integer)
|
334
334
|
raise TypeError unless int.is_a?(Integer)
|
@@ -442,27 +442,27 @@ module Process
|
|
442
442
|
strcpy(sid_buf, sid_ptr.unpack('L')[0])
|
443
443
|
sid_buf.strip.split('-').last.to_i
|
444
444
|
end
|
445
|
-
end
|
445
|
+
end
|
446
446
|
|
447
447
|
# Waits for the given child process to exit and returns that pid.
|
448
|
-
#
|
448
|
+
#
|
449
449
|
# The +flags+ argument is ignored at the moment. It is provided strictly
|
450
450
|
# for interface compatibility.
|
451
451
|
#
|
452
452
|
# Note that the $? (Process::Status) global variable is NOT set. This
|
453
453
|
# may be addressed in a future release.
|
454
|
-
#
|
454
|
+
#
|
455
455
|
def waitpid(pid, flags = nil)
|
456
456
|
exit_code = [0].pack('L')
|
457
457
|
handle = OpenProcess(PROCESS_ALL_ACCESS, 0, pid)
|
458
|
-
|
458
|
+
|
459
459
|
if handle == INVALID_HANDLE_VALUE
|
460
460
|
raise Error, get_last_error
|
461
461
|
end
|
462
|
-
|
462
|
+
|
463
463
|
# TODO: update the $? global variable (if/when possible)
|
464
464
|
status = WaitForSingleObject(handle, INFINITE)
|
465
|
-
|
465
|
+
|
466
466
|
begin
|
467
467
|
unless GetExitCodeProcess(handle, exit_code)
|
468
468
|
raise Error, get_last_error
|
@@ -470,38 +470,38 @@ module Process
|
|
470
470
|
ensure
|
471
471
|
CloseHandle(handle)
|
472
472
|
end
|
473
|
-
|
473
|
+
|
474
474
|
@child_pids.delete(pid)
|
475
|
-
|
475
|
+
|
476
476
|
# TODO: update the $? global variable (if/when possible)
|
477
477
|
exit_code = exit_code.unpack('L').first
|
478
|
-
|
478
|
+
|
479
479
|
pid
|
480
480
|
end
|
481
|
-
|
481
|
+
|
482
482
|
# Waits for the given child process to exit and returns an array containing
|
483
483
|
# the process id and the exit status.
|
484
484
|
#
|
485
485
|
# The +flags+ argument is ignored at the moment. It is provided strictly
|
486
486
|
# for interface compatibility.
|
487
|
-
#
|
487
|
+
#
|
488
488
|
# Note that the $? (Process::Status) global variable is NOT set. This
|
489
489
|
# may be addressed in a future release if/when possible.
|
490
490
|
#--
|
491
491
|
# Ruby does not provide a way to hook into $? so there's no way for us
|
492
492
|
# to set it.
|
493
|
-
#
|
493
|
+
#
|
494
494
|
def waitpid2(pid, flags = nil)
|
495
495
|
exit_code = [0].pack('L')
|
496
496
|
handle = OpenProcess(PROCESS_ALL_ACCESS, 0, pid)
|
497
|
-
|
497
|
+
|
498
498
|
if handle == INVALID_HANDLE_VALUE
|
499
499
|
raise Error, get_last_error
|
500
500
|
end
|
501
|
-
|
501
|
+
|
502
502
|
# TODO: update the $? global variable (if/when possible)
|
503
503
|
status = WaitForSingleObject(handle, INFINITE)
|
504
|
-
|
504
|
+
|
505
505
|
begin
|
506
506
|
unless GetExitCodeProcess(handle, exit_code)
|
507
507
|
raise Error, get_last_error
|
@@ -509,33 +509,33 @@ module Process
|
|
509
509
|
ensure
|
510
510
|
CloseHandle(handle)
|
511
511
|
end
|
512
|
-
|
512
|
+
|
513
513
|
@child_pids.delete(pid)
|
514
|
-
|
514
|
+
|
515
515
|
# TODO: update the $? global variable (if/when possible)
|
516
516
|
exit_code = exit_code.unpack('L').first
|
517
|
-
|
517
|
+
|
518
518
|
[pid, exit_code]
|
519
519
|
end
|
520
|
-
|
520
|
+
|
521
521
|
# Sends the given +signal+ to an array of process id's. The +signal+ may
|
522
522
|
# be any value from 0 to 9, or the special strings 'SIGINT' (or 'INT'),
|
523
523
|
# 'SIGBRK' (or 'BRK') and 'SIGKILL' (or 'KILL'). An array of successfully
|
524
524
|
# killed pids is returned.
|
525
|
-
#
|
525
|
+
#
|
526
526
|
# Signal 0 merely tests if the process is running without killing it.
|
527
527
|
# Signal 2 sends a CTRL_C_EVENT to the process.
|
528
528
|
# Signal 3 sends a CTRL_BRK_EVENT to the process.
|
529
529
|
# Signal 9 kills the process in a harsh manner.
|
530
530
|
# Signals 1 and 4-8 kill the process in a nice manner.
|
531
|
-
#
|
531
|
+
#
|
532
532
|
# SIGINT/INT corresponds to signal 2
|
533
533
|
# SIGBRK/BRK corresponds to signal 3
|
534
534
|
# SIGKILL/KILL corresponds to signal 9
|
535
|
-
#
|
535
|
+
#
|
536
536
|
# Signals 2 and 3 only affect console processes, and then only if the
|
537
537
|
# process was created with the CREATE_NEW_PROCESS_GROUP flag.
|
538
|
-
#
|
538
|
+
#
|
539
539
|
def kill(signal, *pids)
|
540
540
|
case signal
|
541
541
|
when 'SIGINT', 'INT'
|
@@ -549,15 +549,15 @@ module Process
|
|
549
549
|
else
|
550
550
|
raise Error, "Invalid signal '#{signal}'"
|
551
551
|
end
|
552
|
-
|
552
|
+
|
553
553
|
killed_pids = []
|
554
|
-
|
554
|
+
|
555
555
|
pids.each{ |pid|
|
556
556
|
# Send the signal to the current process if the pid is zero
|
557
557
|
if pid == 0
|
558
558
|
pid = Process.pid
|
559
559
|
end
|
560
|
-
|
560
|
+
|
561
561
|
# No need for full access if the signal is zero
|
562
562
|
if signal == 0
|
563
563
|
access = PROCESS_QUERY_INFORMATION | PROCESS_VM_READ
|
@@ -565,10 +565,10 @@ module Process
|
|
565
565
|
else
|
566
566
|
handle = OpenProcess(PROCESS_ALL_ACCESS, 0, pid)
|
567
567
|
end
|
568
|
-
|
568
|
+
|
569
569
|
begin
|
570
570
|
case signal
|
571
|
-
when 0
|
571
|
+
when 0
|
572
572
|
if handle != 0
|
573
573
|
killed_pids.push(pid)
|
574
574
|
else
|
@@ -590,14 +590,14 @@ module Process
|
|
590
590
|
when 9
|
591
591
|
if TerminateProcess(handle, pid)
|
592
592
|
killed_pids.push(pid)
|
593
|
-
@child_pids.delete(pid)
|
593
|
+
@child_pids.delete(pid)
|
594
594
|
else
|
595
595
|
raise Error, get_last_error
|
596
596
|
end
|
597
597
|
else
|
598
598
|
if handle != 0
|
599
599
|
thread_id = [0].pack('L')
|
600
|
-
begin
|
600
|
+
begin
|
601
601
|
thread = CreateRemoteThread(
|
602
602
|
handle,
|
603
603
|
0,
|
@@ -620,7 +620,7 @@ module Process
|
|
620
620
|
end
|
621
621
|
else
|
622
622
|
raise Error, get_last_error
|
623
|
-
|
623
|
+
end # case
|
624
624
|
|
625
625
|
@child_pids.delete(pid)
|
626
626
|
end
|
@@ -628,12 +628,12 @@ module Process
|
|
628
628
|
CloseHandle(handle) unless handle == INVALID_HANDLE_VALUE
|
629
629
|
end
|
630
630
|
}
|
631
|
-
|
631
|
+
|
632
632
|
killed_pids
|
633
633
|
end
|
634
|
-
|
634
|
+
|
635
635
|
# Process.create(key => value, ...) => ProcessInfo
|
636
|
-
#
|
636
|
+
#
|
637
637
|
# This is a wrapper for the CreateProcess() function. It executes a process,
|
638
638
|
# returning a ProcessInfo struct. It accepts a hash as an argument.
|
639
639
|
# There are several primary keys:
|
@@ -664,7 +664,7 @@ module Process
|
|
664
664
|
# part of the StartupInfo struct, and are generally only meaningful for
|
665
665
|
# GUI or console processes. See the documentation on CreateProcess()
|
666
666
|
# and the StartupInfo struct on MSDN for more information.
|
667
|
-
#
|
667
|
+
#
|
668
668
|
# * desktop
|
669
669
|
# * title
|
670
670
|
# * x
|
@@ -679,7 +679,7 @@ module Process
|
|
679
679
|
# * stdin
|
680
680
|
# * stdout
|
681
681
|
# * stderr
|
682
|
-
#
|
682
|
+
#
|
683
683
|
# The relevant constants for 'creation_flags', 'sw_flags' and 'startf_flags'
|
684
684
|
# are included in the Windows::Process, Windows::Console and Windows::Window
|
685
685
|
# modules. These come with the windows-pr library, a prerequisite of this
|
@@ -690,9 +690,9 @@ module Process
|
|
690
690
|
# If 'stdin', 'stdout' or 'stderr' are specified, then the +inherit+ value
|
691
691
|
# is automatically set to true and the Process::STARTF_USESTDHANDLES flag is
|
692
692
|
# automatically OR'd to the +startf_flags+ value.
|
693
|
-
#
|
693
|
+
#
|
694
694
|
# The ProcessInfo struct contains the following members:
|
695
|
-
#
|
695
|
+
#
|
696
696
|
# * process_handle - The handle to the newly created process.
|
697
697
|
# * thread_handle - The handle to the primary thread of the process.
|
698
698
|
# * process_id - Process ID.
|
@@ -709,7 +709,7 @@ module Process
|
|
709
709
|
unless args.kind_of?(Hash)
|
710
710
|
raise TypeError, 'Expecting hash-style keyword arguments'
|
711
711
|
end
|
712
|
-
|
712
|
+
|
713
713
|
valid_keys = %w/
|
714
714
|
app_name command_line inherit creation_flags cwd environment
|
715
715
|
startup_info thread_inherit process_inherit close_handles with_logon
|
@@ -727,8 +727,8 @@ module Process
|
|
727
727
|
'creation_flags' => 0,
|
728
728
|
'close_handles' => true
|
729
729
|
}
|
730
|
-
|
731
|
-
# Validate the keys, and convert symbols and case to lowercase strings.
|
730
|
+
|
731
|
+
# Validate the keys, and convert symbols and case to lowercase strings.
|
732
732
|
args.each{ |key, val|
|
733
733
|
key = key.to_s.downcase
|
734
734
|
unless valid_keys.include?(key)
|
@@ -736,9 +736,9 @@ module Process
|
|
736
736
|
end
|
737
737
|
hash[key] = val
|
738
738
|
}
|
739
|
-
|
739
|
+
|
740
740
|
si_hash = {}
|
741
|
-
|
741
|
+
|
742
742
|
# If the startup_info key is present, validate its subkeys
|
743
743
|
if hash['startup_info']
|
744
744
|
hash['startup_info'].each{ |key, val|
|
@@ -749,7 +749,7 @@ module Process
|
|
749
749
|
si_hash[key] = val
|
750
750
|
}
|
751
751
|
end
|
752
|
-
|
752
|
+
|
753
753
|
# The +command_line+ key is mandatory unless the +app_name+ key
|
754
754
|
# is specified.
|
755
755
|
unless hash['command_line']
|
@@ -760,14 +760,14 @@ module Process
|
|
760
760
|
raise ArgumentError, 'command_line or app_name must be specified'
|
761
761
|
end
|
762
762
|
end
|
763
|
-
|
763
|
+
|
764
764
|
# The environment string should be passed as a string of ';' separated
|
765
765
|
# paths.
|
766
|
-
if hash['environment']
|
766
|
+
if hash['environment']
|
767
767
|
env = hash['environment'].split(File::PATH_SEPARATOR) << 0.chr
|
768
768
|
if hash['with_logon']
|
769
769
|
env = env.map{ |e| multi_to_wide(e) }
|
770
|
-
env = [env.join("\0\0")].pack('p*').unpack('L').first
|
770
|
+
env = [env.join("\0\0")].pack('p*').unpack('L').first
|
771
771
|
else
|
772
772
|
env = [env.join("\0")].pack('p*').unpack('L').first
|
773
773
|
end
|
@@ -804,7 +804,7 @@ module Process
|
|
804
804
|
else
|
805
805
|
handle = get_osfhandle(si_hash[io])
|
806
806
|
end
|
807
|
-
|
807
|
+
|
808
808
|
if handle == INVALID_HANDLE_VALUE
|
809
809
|
raise Error, get_last_error
|
810
810
|
end
|
@@ -818,14 +818,14 @@ module Process
|
|
818
818
|
)
|
819
819
|
|
820
820
|
raise Error, get_last_error unless bool
|
821
|
-
|
821
|
+
|
822
822
|
si_hash[io] = handle
|
823
823
|
si_hash['startf_flags'] ||= 0
|
824
824
|
si_hash['startf_flags'] |= STARTF_USESTDHANDLES
|
825
825
|
hash['inherit'] = true
|
826
826
|
end
|
827
827
|
}
|
828
|
-
|
828
|
+
|
829
829
|
# The bytes not covered here are reserved (null)
|
830
830
|
unless si_hash.empty?
|
831
831
|
startinfo[0,4] = [startinfo.size].pack('L')
|
@@ -842,7 +842,7 @@ module Process
|
|
842
842
|
startinfo[48,2] = [si_hash['sw_flags']].pack('S') if si_hash['sw_flags']
|
843
843
|
startinfo[56,4] = [si_hash['stdin']].pack('L') if si_hash['stdin']
|
844
844
|
startinfo[60,4] = [si_hash['stdout']].pack('L') if si_hash['stdout']
|
845
|
-
startinfo[64,4] = [si_hash['stderr']].pack('L') if si_hash['stderr']
|
845
|
+
startinfo[64,4] = [si_hash['stderr']].pack('L') if si_hash['stderr']
|
846
846
|
end
|
847
847
|
|
848
848
|
if hash['with_logon']
|
@@ -852,7 +852,7 @@ module Process
|
|
852
852
|
cmd = hash['command_line'].nil? ? nil : multi_to_wide(hash['command_line'])
|
853
853
|
cwd = multi_to_wide(hash['cwd'])
|
854
854
|
passwd = multi_to_wide(hash['password'])
|
855
|
-
|
855
|
+
|
856
856
|
hash['creation_flags'] |= CREATE_UNICODE_ENVIRONMENT
|
857
857
|
|
858
858
|
bool = CreateProcessWithLogonW(
|
@@ -868,7 +868,7 @@ module Process
|
|
868
868
|
startinfo, # Startup Info
|
869
869
|
procinfo # Process Info
|
870
870
|
)
|
871
|
-
else
|
871
|
+
else
|
872
872
|
bool = CreateProcess(
|
873
873
|
hash['app_name'], # App name
|
874
874
|
hash['command_line'], # Command line
|
@@ -881,21 +881,21 @@ module Process
|
|
881
881
|
startinfo, # Startup Info
|
882
882
|
procinfo # Process Info
|
883
883
|
)
|
884
|
-
end
|
885
|
-
|
884
|
+
end
|
885
|
+
|
886
886
|
# TODO: Close stdin, stdout and stderr handles in the si_hash unless
|
887
887
|
# they're pointing to one of the standard handles already. [Maybe]
|
888
888
|
unless bool
|
889
889
|
raise Error, "CreateProcess() failed: " + get_last_error
|
890
890
|
end
|
891
|
-
|
891
|
+
|
892
892
|
# Automatically close the process and thread handles in the
|
893
893
|
# PROCESS_INFORMATION struct unless explicitly told not to.
|
894
894
|
if hash['close_handles']
|
895
895
|
CloseHandle(procinfo[0,4].unpack('L').first)
|
896
896
|
CloseHandle(procinfo[4,4].unpack('L').first)
|
897
|
-
end
|
898
|
-
|
897
|
+
end
|
898
|
+
|
899
899
|
ProcessInfo.new(
|
900
900
|
procinfo[0,4].unpack('L').first, # hProcess
|
901
901
|
procinfo[4,4].unpack('L').first, # hThread
|
@@ -903,20 +903,20 @@ module Process
|
|
903
903
|
procinfo[12,4].unpack('L').first # hThreadId
|
904
904
|
)
|
905
905
|
end
|
906
|
-
|
906
|
+
|
907
907
|
# Waits for any child process to exit and returns the process id of that
|
908
908
|
# child. If a pid is provided that is greater than or equal to 0, then
|
909
909
|
# it is the equivalent of calling Process.waitpid.
|
910
910
|
#
|
911
911
|
# The +flags+ argument is ignored at the moment. It is provided strictly
|
912
912
|
# for interface compatibility.
|
913
|
-
#
|
913
|
+
#
|
914
914
|
# Note that the $? (Process::Status) global variable is NOT set. This
|
915
915
|
# may be addressed in a future release.
|
916
916
|
#--
|
917
917
|
# The GetProcessId() function is not defined in Windows 2000 or earlier
|
918
918
|
# so we have to do some extra work for those platforms.
|
919
|
-
#
|
919
|
+
#
|
920
920
|
def wait(pid = -1, flags = nil)
|
921
921
|
if pid && pid >= 0
|
922
922
|
pid, status = waitpid(pid, flags)
|
@@ -924,62 +924,62 @@ module Process
|
|
924
924
|
end
|
925
925
|
|
926
926
|
handles = []
|
927
|
-
|
927
|
+
|
928
928
|
# Windows 2000 or earlier
|
929
929
|
unless defined? GetProcessId
|
930
930
|
pids = []
|
931
931
|
end
|
932
|
-
|
933
|
-
@child_pids.each_with_index{ |
|
934
|
-
handles[i] = OpenProcess(PROCESS_ALL_ACCESS, 0,
|
935
|
-
|
932
|
+
|
933
|
+
@child_pids.each_with_index{ |lpid, i|
|
934
|
+
handles[i] = OpenProcess(PROCESS_ALL_ACCESS, 0, lpid)
|
935
|
+
|
936
936
|
if handles[i] == INVALID_HANDLE_VALUE
|
937
|
-
err = "unable to get HANDLE on process associated with pid #{
|
937
|
+
err = "unable to get HANDLE on process associated with pid #{lpid}"
|
938
938
|
raise Error, err
|
939
939
|
end
|
940
|
-
|
940
|
+
|
941
941
|
unless defined? GetProcessId
|
942
|
-
pids[i] =
|
943
|
-
end
|
942
|
+
pids[i] = lpid
|
943
|
+
end
|
944
944
|
}
|
945
|
-
|
945
|
+
|
946
946
|
wait = WaitForMultipleObjects(
|
947
947
|
handles.size,
|
948
948
|
handles.pack('L*'),
|
949
949
|
0,
|
950
950
|
INFINITE
|
951
951
|
)
|
952
|
-
|
952
|
+
|
953
953
|
if wait >= WAIT_OBJECT_0 && wait <= WAIT_OBJECT_0 + @child_pids.size - 1
|
954
|
-
index = wait - WAIT_OBJECT_0
|
954
|
+
index = wait - WAIT_OBJECT_0
|
955
955
|
handle = handles[index]
|
956
|
-
|
956
|
+
|
957
957
|
if defined? GetProcessId
|
958
958
|
pid = GetProcessId(handle)
|
959
959
|
else
|
960
960
|
pid = pids[index]
|
961
961
|
end
|
962
|
-
|
962
|
+
|
963
963
|
@child_pids.delete(pid)
|
964
|
-
handles.each{ |
|
964
|
+
handles.each{ |h| CloseHandle(h) }
|
965
965
|
return pid
|
966
966
|
end
|
967
|
-
|
967
|
+
|
968
968
|
nil
|
969
969
|
end
|
970
|
-
|
970
|
+
|
971
971
|
# Waits for any child process to exit and returns an array containing the
|
972
972
|
# process id and the exit status of that child.
|
973
973
|
#
|
974
974
|
# The +flags+ argument is ignored at the moment. It is provided strictly
|
975
975
|
# for interface compatibility.
|
976
|
-
#
|
976
|
+
#
|
977
977
|
# Note that the $? (Process::Status) global variable is NOT set. This
|
978
978
|
# may be addressed in a future release.
|
979
979
|
#--
|
980
980
|
# The GetProcessId() function is not defined in Windows 2000 or earlier
|
981
981
|
# so we have to do some extra work for those platforms.
|
982
|
-
#
|
982
|
+
#
|
983
983
|
def wait2(pid = -1, flags = nil)
|
984
984
|
if pid && pid >= 0
|
985
985
|
pid, status = waitpid2(pid, flags)
|
@@ -987,54 +987,54 @@ module Process
|
|
987
987
|
end
|
988
988
|
|
989
989
|
handles = []
|
990
|
-
|
990
|
+
|
991
991
|
# Windows 2000 or earlier
|
992
992
|
unless defined? GetProcessId
|
993
993
|
pids = []
|
994
994
|
end
|
995
|
-
|
996
|
-
@child_pids.each_with_index{ |
|
997
|
-
handles[i] = OpenProcess(PROCESS_ALL_ACCESS, 0,
|
998
|
-
|
995
|
+
|
996
|
+
@child_pids.each_with_index{ |lpid, i|
|
997
|
+
handles[i] = OpenProcess(PROCESS_ALL_ACCESS, 0, lpid)
|
998
|
+
|
999
999
|
if handles[i] == INVALID_HANDLE_VALUE
|
1000
|
-
err = "unable to get HANDLE on process associated with pid #{
|
1000
|
+
err = "unable to get HANDLE on process associated with pid #{lpid}"
|
1001
1001
|
raise Error, err
|
1002
1002
|
end
|
1003
|
-
|
1003
|
+
|
1004
1004
|
unless defined? GetProcessId
|
1005
|
-
pids[i] =
|
1006
|
-
end
|
1005
|
+
pids[i] = lpid
|
1006
|
+
end
|
1007
1007
|
}
|
1008
|
-
|
1008
|
+
|
1009
1009
|
wait = WaitForMultipleObjects(
|
1010
1010
|
handles.size,
|
1011
1011
|
handles.pack('L*'),
|
1012
1012
|
0,
|
1013
1013
|
INFINITE
|
1014
1014
|
)
|
1015
|
-
|
1015
|
+
|
1016
1016
|
if wait >= WAIT_OBJECT_0 && wait <= WAIT_OBJECT_0 + @child_pids.size - 1
|
1017
|
-
index = wait - WAIT_OBJECT_0
|
1017
|
+
index = wait - WAIT_OBJECT_0
|
1018
1018
|
handle = handles[index]
|
1019
|
-
|
1019
|
+
|
1020
1020
|
if defined? GetProcessId
|
1021
1021
|
pid = GetProcessId(handle)
|
1022
1022
|
else
|
1023
1023
|
pid = pids[index]
|
1024
1024
|
end
|
1025
|
-
|
1025
|
+
|
1026
1026
|
exit_code = [0].pack('l')
|
1027
1027
|
|
1028
1028
|
unless GetExitCodeProcess(handle, exit_code)
|
1029
1029
|
raise get_last_error
|
1030
1030
|
end
|
1031
|
-
|
1031
|
+
|
1032
1032
|
@child_pids.delete(pid)
|
1033
|
-
|
1034
|
-
handles.each{ |
|
1033
|
+
|
1034
|
+
handles.each{ |h| CloseHandle(h) }
|
1035
1035
|
return [pid, exit_code.unpack('l').first]
|
1036
1036
|
end
|
1037
|
-
|
1037
|
+
|
1038
1038
|
nil
|
1039
1039
|
end
|
1040
1040
|
|
@@ -1055,8 +1055,8 @@ module Process
|
|
1055
1055
|
|
1056
1056
|
proc_entry = 0.chr * 296 # 36 + 260
|
1057
1057
|
proc_entry[0, 4] = [proc_entry.size].pack('L') # Set dwSize member
|
1058
|
-
|
1059
|
-
begin
|
1058
|
+
|
1059
|
+
begin
|
1060
1060
|
unless Process32First(handle, proc_entry)
|
1061
1061
|
error = get_last_error
|
1062
1062
|
raise Error, error
|
@@ -1082,10 +1082,10 @@ module Process
|
|
1082
1082
|
#
|
1083
1083
|
# WARNING: This implementation should be considered experimental. It is
|
1084
1084
|
# not recommended for production use.
|
1085
|
-
#
|
1085
|
+
#
|
1086
1086
|
def fork
|
1087
1087
|
last_arg = ARGV.last
|
1088
|
-
|
1088
|
+
|
1089
1089
|
# Look for the 'child#xxx' tag
|
1090
1090
|
if last_arg =~ /child#\d+/
|
1091
1091
|
@i += 1
|
@@ -1106,29 +1106,29 @@ module Process
|
|
1106
1106
|
return false
|
1107
1107
|
end
|
1108
1108
|
end
|
1109
|
-
|
1109
|
+
|
1110
1110
|
# Tag the command with the word 'child#xxx' to distinguish it
|
1111
1111
|
# from the calling process.
|
1112
1112
|
cmd = 'ruby -I "' + $LOAD_PATH.join(File::PATH_SEPARATOR) << '" "'
|
1113
1113
|
cmd << File.expand_path($PROGRAM_NAME) << '" ' << ARGV.join(' ')
|
1114
1114
|
cmd << ' child#' << @child_pids.length.to_s
|
1115
|
-
|
1115
|
+
|
1116
1116
|
startinfo = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
|
1117
1117
|
startinfo = startinfo.pack('LLLLLLLLLLLLSSLLLL')
|
1118
1118
|
procinfo = [0,0,0,0].pack('LLLL')
|
1119
|
-
|
1120
|
-
rv = CreateProcess(
|
1121
|
-
|
1119
|
+
|
1120
|
+
rv = CreateProcess(nil, cmd, 0, 0, 1, 0, 0, 0, startinfo, procinfo)
|
1121
|
+
|
1122
1122
|
if rv == 0
|
1123
1123
|
raise Error, get_last_error
|
1124
1124
|
end
|
1125
|
-
|
1125
|
+
|
1126
1126
|
pid = procinfo[8,4].unpack('L').first
|
1127
1127
|
@child_pids.push(pid)
|
1128
|
-
|
1129
|
-
pid
|
1128
|
+
|
1129
|
+
pid
|
1130
1130
|
end
|
1131
|
-
|
1131
|
+
|
1132
1132
|
module_function :create, :fork, :get_affinity, :getrlimit, :getpriority
|
1133
1133
|
module_function :job?, :kill, :ppid, :setpriority, :setrlimit
|
1134
1134
|
module_function :wait, :wait2, :waitpid, :waitpid2, :uid
|
data/test/test_win32_process.rb
CHANGED
@@ -11,21 +11,18 @@
|
|
11
11
|
#
|
12
12
|
# You should run this test case via the 'rake test' task.
|
13
13
|
###############################################################################
|
14
|
-
require '
|
15
|
-
gem 'test-unit'
|
16
|
-
|
17
|
-
require 'test/unit'
|
14
|
+
require 'test-unit'
|
18
15
|
require 'win32/process'
|
19
16
|
require 'sys/proctable'
|
20
17
|
|
21
|
-
class TC_Win32Process < Test::Unit::TestCase
|
18
|
+
class TC_Win32Process < Test::Unit::TestCase
|
22
19
|
|
23
20
|
# Start two instances of notepad and give them a chance to fire up
|
24
21
|
def self.startup
|
25
22
|
IO.popen('notepad')
|
26
23
|
IO.popen('notepad')
|
27
24
|
sleep 1 # Give the notepad instances a second to startup
|
28
|
-
|
25
|
+
|
29
26
|
@@pids = []
|
30
27
|
|
31
28
|
Sys::ProcTable.ps{ |struct|
|
@@ -37,15 +34,15 @@ class TC_Win32Process < Test::Unit::TestCase
|
|
37
34
|
def setup
|
38
35
|
@pri_class = Process::NORMAL_PRIORITY_CLASS
|
39
36
|
end
|
40
|
-
|
37
|
+
|
41
38
|
test "win32-process version is set to the correct value" do
|
42
|
-
assert_equal('0.6.
|
39
|
+
assert_equal('0.6.6', Process::WIN32_PROCESS_VERSION)
|
43
40
|
end
|
44
|
-
|
41
|
+
|
45
42
|
test "kill basic functionality" do
|
46
43
|
assert_respond_to(Process, :kill)
|
47
44
|
end
|
48
|
-
|
45
|
+
|
49
46
|
test "kill requires at least one argument" do
|
50
47
|
assert_raises(ArgumentError){ Process.kill }
|
51
48
|
end
|
@@ -57,19 +54,19 @@ class TC_Win32Process < Test::Unit::TestCase
|
|
57
54
|
test "kill raises an error if an invalid process id is provided" do
|
58
55
|
assert_raises(Process::Error){ Process.kill(0, 9999999) }
|
59
56
|
end
|
60
|
-
|
57
|
+
|
61
58
|
test "kill with signal 0 does not kill the process" do
|
62
59
|
pid = @@pids.first
|
63
60
|
assert_nothing_raised{ Process.kill(0, pid) }
|
64
61
|
assert_not_nil(Sys::ProcTable.ps(pid))
|
65
62
|
end
|
66
|
-
|
63
|
+
|
67
64
|
test "kill with signal 1 kills the process normally" do
|
68
65
|
pid = @@pids.shift
|
69
66
|
assert_nothing_raised{ Process.kill(1, pid) }
|
70
67
|
assert_nil(Sys::ProcTable.ps(pid))
|
71
68
|
end
|
72
|
-
|
69
|
+
|
73
70
|
test "kill with signal 9 kills the process brutally" do
|
74
71
|
pid = @@pids.pop
|
75
72
|
msg = "Could not find pid #{pid}"
|
@@ -80,7 +77,7 @@ class TC_Win32Process < Test::Unit::TestCase
|
|
80
77
|
test "fork basic functionality" do
|
81
78
|
assert_respond_to(Process, :fork)
|
82
79
|
end
|
83
|
-
|
80
|
+
|
84
81
|
test "create basic functionality" do
|
85
82
|
assert_respond_to(Process, :create)
|
86
83
|
end
|
@@ -98,7 +95,7 @@ class TC_Win32Process < Test::Unit::TestCase
|
|
98
95
|
|
99
96
|
assert_nothing_raised{ Process.kill(1, @@pids.pop) }
|
100
97
|
end
|
101
|
-
|
98
|
+
|
102
99
|
test "create requires a hash argument" do
|
103
100
|
assert_raise(TypeError){ Process.create("bogusapp.exe") }
|
104
101
|
end
|
@@ -124,19 +121,19 @@ class TC_Win32Process < Test::Unit::TestCase
|
|
124
121
|
assert_raise(Process::Error){ Process.create(:app_name => "bogusapp.exe") }
|
125
122
|
assert_raise_message(err){ Process.create(:app_name => "bogusapp.exe") }
|
126
123
|
end
|
127
|
-
|
124
|
+
|
128
125
|
test "wait basic functionality" do
|
129
126
|
assert_respond_to(Process, :wait)
|
130
127
|
end
|
131
|
-
|
128
|
+
|
132
129
|
test "wait2 basic functionality" do
|
133
130
|
assert_respond_to(Process, :wait2)
|
134
131
|
end
|
135
|
-
|
132
|
+
|
136
133
|
test "waitpid basic functionality" do
|
137
134
|
assert_respond_to(Process, :waitpid)
|
138
135
|
end
|
139
|
-
|
136
|
+
|
140
137
|
test "waitpid2 basic functionality" do
|
141
138
|
assert_respond_to(Process, :waitpid2)
|
142
139
|
end
|
@@ -151,19 +148,19 @@ class TC_Win32Process < Test::Unit::TestCase
|
|
151
148
|
assert_true(Process.ppid > 0)
|
152
149
|
assert_false(Process.pid == Process.ppid)
|
153
150
|
end
|
154
|
-
|
151
|
+
|
155
152
|
test "uid basic functionality" do
|
156
153
|
assert_respond_to(Process, :uid)
|
157
154
|
assert_kind_of(Fixnum, Process.uid)
|
158
155
|
end
|
159
156
|
|
160
|
-
test "uid accepts a boolean argument" do
|
157
|
+
test "uid accepts a boolean argument" do
|
161
158
|
assert_nothing_raised{ Process.uid(true) }
|
162
159
|
assert_nothing_raised{ Process.uid(true) }
|
163
160
|
end
|
164
161
|
|
165
162
|
test "uid returns a string if its argument is true" do
|
166
|
-
assert_kind_of(String, Process.uid(true))
|
163
|
+
assert_kind_of(String, Process.uid(true))
|
167
164
|
end
|
168
165
|
|
169
166
|
test "uid accepts a maximum of one argument" do
|
@@ -284,7 +281,7 @@ class TC_Win32Process < Test::Unit::TestCase
|
|
284
281
|
test "is_job does not accept any arguments" do
|
285
282
|
assert_raise(ArgumentError){ Process.job?(Process.pid) }
|
286
283
|
end
|
287
|
-
|
284
|
+
|
288
285
|
def teardown
|
289
286
|
@pri_class = nil
|
290
287
|
end
|
data/win32-process.gemspec
CHANGED
@@ -2,7 +2,7 @@ require 'rubygems'
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |spec|
|
4
4
|
spec.name = 'win32-process'
|
5
|
-
spec.version = '0.6.
|
5
|
+
spec.version = '0.6.6'
|
6
6
|
spec.license = 'Artistic 2.0'
|
7
7
|
spec.authors = ['Daniel Berger', 'Park Heesob']
|
8
8
|
spec.email = 'djberg96@gmail.com'
|
@@ -10,14 +10,13 @@ Gem::Specification.new do |spec|
|
|
10
10
|
spec.platform = Gem::Platform::RUBY
|
11
11
|
spec.summary = 'Adds and redefines several Process methods for MS Windows'
|
12
12
|
spec.test_files = Dir['test/*.rb']
|
13
|
-
spec.has_rdoc = true
|
14
13
|
spec.files = Dir['**/*'].reject{ |f| f.include?('git') }
|
15
14
|
|
16
15
|
spec.rubyforge_project = 'win32utils'
|
17
16
|
spec.extra_rdoc_files = ['README', 'CHANGES', 'MANIFEST']
|
18
17
|
|
19
|
-
spec.add_dependency('windows-pr', '>= 1.
|
20
|
-
spec.add_development_dependency('test-unit'
|
18
|
+
spec.add_dependency('windows-pr', '>= 1.2.2')
|
19
|
+
spec.add_development_dependency('test-unit')
|
21
20
|
spec.add_development_dependency('sys-proctable')
|
22
21
|
|
23
22
|
spec.description = <<-EOF
|
metadata
CHANGED
@@ -1,81 +1,77 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: win32-process
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
prerelease:
|
6
|
-
segments:
|
7
|
-
- 0
|
8
|
-
- 6
|
9
|
-
- 5
|
10
|
-
version: 0.6.5
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.6.6
|
5
|
+
prerelease:
|
11
6
|
platform: ruby
|
12
|
-
authors:
|
7
|
+
authors:
|
13
8
|
- Daniel Berger
|
14
9
|
- Park Heesob
|
15
10
|
autorequire:
|
16
11
|
bindir: bin
|
17
12
|
cert_chain: []
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
dependencies:
|
22
|
-
- !ruby/object:Gem::Dependency
|
13
|
+
date: 2012-07-19 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
23
16
|
name: windows-pr
|
24
|
-
|
25
|
-
requirement: &id001 !ruby/object:Gem::Requirement
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
26
18
|
none: false
|
27
|
-
requirements:
|
28
|
-
- -
|
29
|
-
- !ruby/object:Gem::Version
|
30
|
-
|
31
|
-
segments:
|
32
|
-
- 1
|
33
|
-
- 1
|
34
|
-
- 2
|
35
|
-
version: 1.1.2
|
19
|
+
requirements:
|
20
|
+
- - ! '>='
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 1.2.2
|
36
23
|
type: :runtime
|
37
|
-
version_requirements: *id001
|
38
|
-
- !ruby/object:Gem::Dependency
|
39
|
-
name: test-unit
|
40
24
|
prerelease: false
|
41
|
-
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
27
|
+
requirements:
|
28
|
+
- - ! '>='
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
version: 1.2.2
|
31
|
+
- !ruby/object:Gem::Dependency
|
32
|
+
name: test-unit
|
33
|
+
requirement: !ruby/object:Gem::Requirement
|
42
34
|
none: false
|
43
|
-
requirements:
|
44
|
-
- -
|
45
|
-
- !ruby/object:Gem::Version
|
46
|
-
|
47
|
-
segments:
|
48
|
-
- 2
|
49
|
-
- 1
|
50
|
-
- 1
|
51
|
-
version: 2.1.1
|
35
|
+
requirements:
|
36
|
+
- - ! '>='
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: '0'
|
52
39
|
type: :development
|
53
|
-
version_requirements: *id002
|
54
|
-
- !ruby/object:Gem::Dependency
|
55
|
-
name: sys-proctable
|
56
40
|
prerelease: false
|
57
|
-
|
41
|
+
version_requirements: !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ! '>='
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: sys-proctable
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
58
50
|
none: false
|
59
|
-
requirements:
|
60
|
-
- -
|
61
|
-
- !ruby/object:Gem::Version
|
62
|
-
|
63
|
-
segments:
|
64
|
-
- 0
|
65
|
-
version: "0"
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
66
55
|
type: :development
|
67
|
-
|
68
|
-
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - ! '>='
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
description: ! " The win32-process library implements several Process methods that
|
64
|
+
are\n either unimplemented or dysfunctional in some way in the default Ruby\n
|
65
|
+
\ implementation. Examples include Process.kill, Process.waitpid,\n Process.create
|
66
|
+
and an experimental Process.fork method.\n"
|
69
67
|
email: djberg96@gmail.com
|
70
68
|
executables: []
|
71
|
-
|
72
69
|
extensions: []
|
73
|
-
|
74
|
-
extra_rdoc_files:
|
70
|
+
extra_rdoc_files:
|
75
71
|
- README
|
76
72
|
- CHANGES
|
77
73
|
- MANIFEST
|
78
|
-
files:
|
74
|
+
files:
|
79
75
|
- CHANGES
|
80
76
|
- examples/example_create.rb
|
81
77
|
- examples/example_fork_wait.rb
|
@@ -87,39 +83,30 @@ files:
|
|
87
83
|
- README
|
88
84
|
- test/test_win32_process.rb
|
89
85
|
- win32-process.gemspec
|
90
|
-
has_rdoc: true
|
91
86
|
homepage: http://www.rubyforge.org/projects/win32utils
|
92
|
-
licenses:
|
87
|
+
licenses:
|
93
88
|
- Artistic 2.0
|
94
89
|
post_install_message:
|
95
90
|
rdoc_options: []
|
96
|
-
|
97
|
-
require_paths:
|
91
|
+
require_paths:
|
98
92
|
- lib
|
99
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
93
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
100
94
|
none: false
|
101
|
-
requirements:
|
102
|
-
- -
|
103
|
-
- !ruby/object:Gem::Version
|
104
|
-
|
105
|
-
|
106
|
-
- 0
|
107
|
-
version: "0"
|
108
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
95
|
+
requirements:
|
96
|
+
- - ! '>='
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: '0'
|
99
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
109
100
|
none: false
|
110
|
-
requirements:
|
111
|
-
- -
|
112
|
-
- !ruby/object:Gem::Version
|
113
|
-
|
114
|
-
segments:
|
115
|
-
- 0
|
116
|
-
version: "0"
|
101
|
+
requirements:
|
102
|
+
- - ! '>='
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: '0'
|
117
105
|
requirements: []
|
118
|
-
|
119
106
|
rubyforge_project: win32utils
|
120
|
-
rubygems_version: 1.
|
107
|
+
rubygems_version: 1.8.24
|
121
108
|
signing_key:
|
122
109
|
specification_version: 3
|
123
110
|
summary: Adds and redefines several Process methods for MS Windows
|
124
|
-
test_files:
|
111
|
+
test_files:
|
125
112
|
- test/test_win32_process.rb
|