win32-process 0.6.5 → 0.6.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|