win32-process 0.7.5 → 0.8.0
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.
- checksums.yaml +4 -4
- data/CHANGES +4 -0
- data/lib/win32/process.rb +202 -2
- data/lib/win32/process/constants.rb +8 -0
- data/lib/win32/process/functions.rb +11 -0
- data/lib/win32/process/helper.rb +1 -1
- data/lib/win32/process/structs.rb +100 -0
- data/test/test_win32_process.rb +18 -1
- data/win32-process.gemspec +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 47640c7bc0c96cd8615348e2f8757c0e1aecec95
|
4
|
+
data.tar.gz: 3f391446c320d0bd0feac23f0effff0bc945ae65
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 63c279644c090065293a308b6b1e68b43c67b7d25c53e0019e82dfc2d1022a6a800ab152b4675188e10077037f82b7cb9f4ecdaae0982c3aee8ba7e81dd6a697
|
7
|
+
data.tar.gz: f5b8a0d72aaa046fdd0a749720f7f5ae61ad99a2b157a3339c8f72cfb35685aa6916522626d036d447f7fc5bf2068cfdf8cd74531e222f6619915dc9fba9a697
|
data/CHANGES
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
= 0.8.0 - 29-Apr-2015
|
2
|
+
* Added the Process.snapshot method that lets you gather information for
|
3
|
+
the heap, threads, modules, and processes.
|
4
|
+
|
1
5
|
= 0.7.5 - 3-Mar-2015
|
2
6
|
* Use require_relative where possible.
|
3
7
|
* Fixed a bug in Process.setrlimit. Note that this method has been marked
|
data/lib/win32/process.rb
CHANGED
@@ -10,7 +10,7 @@ module Process
|
|
10
10
|
extend Process::Constants
|
11
11
|
|
12
12
|
# The version of the win32-process library.
|
13
|
-
WIN32_PROCESS_VERSION = '0.
|
13
|
+
WIN32_PROCESS_VERSION = '0.8.0'
|
14
14
|
|
15
15
|
# Disable popups. This mostly affects the Process.kill method.
|
16
16
|
SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX)
|
@@ -914,6 +914,75 @@ module Process
|
|
914
914
|
exitcode
|
915
915
|
end
|
916
916
|
end
|
917
|
+
|
918
|
+
# Returns a list of process information structs in the form of a hash,
|
919
|
+
# with the pid as the key, and an array of information as the value of
|
920
|
+
# that key. The type of information in that array depends on the
|
921
|
+
# +info_type+ parameter. The possible values for +info_type+, and the
|
922
|
+
# type of information they each return is as follows:
|
923
|
+
#
|
924
|
+
# :thread => ThreadSnapInfo[:thread_id, :process_id, :base_priority]
|
925
|
+
# :heap => HeapSnapInfo[:address, :block_size, :flags, :process_id, :heap_id]
|
926
|
+
# :module => ModuleSnapInfo[:process_id, :address, :module_size, :handle, :name, :path]
|
927
|
+
# :process => ProcessSnapInfo[:process_id, :threads, :parent_process_id, :priority, :flags, :path]
|
928
|
+
#
|
929
|
+
# If no argument is provided, then :thread is assumed. Note that it is up
|
930
|
+
# to you to filter by pid if you wish.
|
931
|
+
#
|
932
|
+
# Example:
|
933
|
+
#
|
934
|
+
# # Get all thread info
|
935
|
+
# Process.snapshot.each{ |pid, v|
|
936
|
+
# puts "PID: #{pid}"
|
937
|
+
# p v
|
938
|
+
# }
|
939
|
+
#
|
940
|
+
# # Get module info for just the current process
|
941
|
+
# p Process.snapshot(:module)[Process.pid]
|
942
|
+
#
|
943
|
+
# # Get heap info for just the current process
|
944
|
+
# p Process.snapshot(:heap)[Process.pid]
|
945
|
+
#
|
946
|
+
# # Show pids of all running processes
|
947
|
+
# p Process.snapshot(:process).keys
|
948
|
+
#
|
949
|
+
def snapshot(info_type = 'thread')
|
950
|
+
case info_type.to_s.downcase
|
951
|
+
when 'thread'
|
952
|
+
flag = TH32CS_SNAPTHREAD
|
953
|
+
when 'heap'
|
954
|
+
flag = TH32CS_SNAPHEAPLIST
|
955
|
+
when 'module'
|
956
|
+
flag = TH32CS_SNAPMODULE
|
957
|
+
when 'process'
|
958
|
+
flag = TH32CS_SNAPPROCESS
|
959
|
+
else
|
960
|
+
raise ArgumentError, "info_type '#{info_type}' unsupported"
|
961
|
+
end
|
962
|
+
|
963
|
+
begin
|
964
|
+
handle = CreateToolhelp32Snapshot(flag, Process.pid)
|
965
|
+
|
966
|
+
if handle == INVALID_HANDLE_VALUE
|
967
|
+
raise SystemCallError.new('CreateToolhelp32Snapshot', FFI.errno)
|
968
|
+
end
|
969
|
+
|
970
|
+
case info_type.to_s.downcase
|
971
|
+
when 'thread'
|
972
|
+
array = get_thread_info(handle)
|
973
|
+
when 'heap'
|
974
|
+
array = get_heap_info(handle)
|
975
|
+
when 'module'
|
976
|
+
array = get_module_info(handle)
|
977
|
+
when 'process'
|
978
|
+
array = get_process_info(handle)
|
979
|
+
end
|
980
|
+
|
981
|
+
array
|
982
|
+
ensure
|
983
|
+
CloseHandle(handle) if handle
|
984
|
+
end
|
985
|
+
end
|
917
986
|
end
|
918
987
|
|
919
988
|
class << self
|
@@ -926,6 +995,137 @@ module Process
|
|
926
995
|
bool ? buf.read_string : nil
|
927
996
|
end
|
928
997
|
|
998
|
+
# Return thread info for Process.snapshot
|
999
|
+
def get_thread_info(handle, pid = nil)
|
1000
|
+
lpte = THREADENTRY32.new
|
1001
|
+
lpte[:dwSize] = lpte.size
|
1002
|
+
|
1003
|
+
hash = Hash.new{ |h,k| h[k] = [] }
|
1004
|
+
|
1005
|
+
if Thread32First(handle, lpte)
|
1006
|
+
hash[lpte[:th32OwnerProcessID]] << ThreadSnapInfo.new(lpte[:th32ThreadID], lpte[:th32OwnerProcessID], lpte[:tpBasePri])
|
1007
|
+
else
|
1008
|
+
if FFI.errno == ERROR_NO_MORE_FILES
|
1009
|
+
return hash
|
1010
|
+
else
|
1011
|
+
raise SystemCallError.new('Thread32First', FFI.errno)
|
1012
|
+
end
|
1013
|
+
end
|
1014
|
+
|
1015
|
+
while Thread32Next(handle, lpte)
|
1016
|
+
hash[lpte[:th32OwnerProcessID]] << ThreadSnapInfo.new(lpte[:th32ThreadID], lpte[:th32OwnerProcessID], lpte[:tpBasePri])
|
1017
|
+
end
|
1018
|
+
|
1019
|
+
hash
|
1020
|
+
end
|
1021
|
+
|
1022
|
+
# Return heap info for Process.snapshot
|
1023
|
+
def get_heap_info(handle)
|
1024
|
+
hash = Hash.new{ |h,k| h[k] = [] }
|
1025
|
+
|
1026
|
+
hl = HEAPLIST32.new
|
1027
|
+
hl[:dwSize] = hl.size
|
1028
|
+
|
1029
|
+
if Heap32ListFirst(handle, hl)
|
1030
|
+
while Heap32ListNext(handle, hl)
|
1031
|
+
he = HEAPENTRY32.new
|
1032
|
+
he[:dwSize] = he.size
|
1033
|
+
|
1034
|
+
if Heap32First(he, Process.pid, hl[:th32HeapID])
|
1035
|
+
hash[he[:th32ProcessID]] << HeapSnapInfo.new(he[:dwAddress], he[:dwBlockSize], he[:dwFlags], he[:th32ProcessID], he[:th32HeapID])
|
1036
|
+
else
|
1037
|
+
if FFI.errno == ERROR_NO_MORE_FILES
|
1038
|
+
break
|
1039
|
+
else
|
1040
|
+
raise SystemCallError.new('Heap32First', FFI.errno)
|
1041
|
+
end
|
1042
|
+
end
|
1043
|
+
|
1044
|
+
while Heap32Next(he)
|
1045
|
+
hash[he[:th32ProcessID]] << HeapSnapInfo.new(he[:dwAddress], he[:dwBlockSize], he[:dwFlags], he[:th32ProcessID], he[:th32HeapID])
|
1046
|
+
end
|
1047
|
+
end
|
1048
|
+
end
|
1049
|
+
|
1050
|
+
hash
|
1051
|
+
end
|
1052
|
+
|
1053
|
+
# Return module info for Process.snapshot
|
1054
|
+
def get_module_info(handle)
|
1055
|
+
hash = Hash.new{ |h,k| h[k] = [] }
|
1056
|
+
|
1057
|
+
me = MODULEENTRY32.new
|
1058
|
+
me[:dwSize] = me.size
|
1059
|
+
|
1060
|
+
if Module32First(handle, me)
|
1061
|
+
hash[me[:th32ProcessID]] << ModuleSnapInfo.new(
|
1062
|
+
me[:th32ProcessID],
|
1063
|
+
me[:modBaseAddr].to_i,
|
1064
|
+
me[:modBaseSize],
|
1065
|
+
me[:hModule],
|
1066
|
+
me[:szModule].to_s,
|
1067
|
+
me[:szExePath].to_s
|
1068
|
+
)
|
1069
|
+
else
|
1070
|
+
if FFI.errno == ERROR_NO_MORE_FILES
|
1071
|
+
return hash
|
1072
|
+
else
|
1073
|
+
raise SystemCallError.new('Module32First', FFI.errno)
|
1074
|
+
end
|
1075
|
+
end
|
1076
|
+
|
1077
|
+
while Module32Next(handle, me)
|
1078
|
+
hash[me[:th32ProcessID]] << ModuleSnapInfo.new(
|
1079
|
+
me[:th32ProcessID],
|
1080
|
+
me[:modBaseAddr].to_i,
|
1081
|
+
me[:modBaseSize],
|
1082
|
+
me[:hModule],
|
1083
|
+
me[:szModule].to_s,
|
1084
|
+
me[:szExePath].to_s
|
1085
|
+
)
|
1086
|
+
end
|
1087
|
+
|
1088
|
+
hash
|
1089
|
+
end
|
1090
|
+
|
1091
|
+
# Return process info for Process.snapshot
|
1092
|
+
def get_process_info(handle)
|
1093
|
+
hash = Hash.new{ |h,k| h[k] = [] }
|
1094
|
+
|
1095
|
+
pe = PROCESSENTRY32.new
|
1096
|
+
pe[:dwSize] = pe.size
|
1097
|
+
|
1098
|
+
if Process32First(handle, pe)
|
1099
|
+
hash[pe[:th32ProcessID]] = ProcessSnapInfo.new(
|
1100
|
+
pe[:th32ProcessID],
|
1101
|
+
pe[:cntThreads],
|
1102
|
+
pe[:th32ParentProcessID],
|
1103
|
+
pe[:pcPriClassBase],
|
1104
|
+
pe[:dwFlags],
|
1105
|
+
pe[:szExeFile].to_s
|
1106
|
+
)
|
1107
|
+
else
|
1108
|
+
if FFI.errno == ERROR_NO_MORE_FILES
|
1109
|
+
return hash
|
1110
|
+
else
|
1111
|
+
raise SystemCallError.new('Process32First', FFI.errno)
|
1112
|
+
end
|
1113
|
+
end
|
1114
|
+
|
1115
|
+
while Process32Next(handle, pe)
|
1116
|
+
hash[pe[:th32ProcessID]] = ProcessSnapInfo.new(
|
1117
|
+
pe[:th32ProcessID],
|
1118
|
+
pe[:cntThreads],
|
1119
|
+
pe[:th32ParentProcessID],
|
1120
|
+
pe[:pcPriClassBase],
|
1121
|
+
pe[:dwFlags],
|
1122
|
+
pe[:szExeFile].to_s
|
1123
|
+
)
|
1124
|
+
end
|
1125
|
+
|
1126
|
+
hash
|
1127
|
+
end
|
1128
|
+
|
929
1129
|
# Private method that returns the Windows major version number.
|
930
1130
|
def windows_version
|
931
1131
|
ver = OSVERSIONINFO.new
|
@@ -938,4 +1138,4 @@ module Process
|
|
938
1138
|
ver[:dwMajorVersion]
|
939
1139
|
end
|
940
1140
|
end
|
941
|
-
end
|
1141
|
+
end
|
@@ -110,4 +110,12 @@ module Process::Constants
|
|
110
110
|
# GetExitCodeProcess
|
111
111
|
|
112
112
|
STILL_ACTIVE = 259
|
113
|
+
|
114
|
+
# Snapshot constants
|
115
|
+
|
116
|
+
TH32CS_SNAPHEAPLIST = 0x00000001
|
117
|
+
TH32CS_SNAPPROCESS = 0x00000002
|
118
|
+
TH32CS_SNAPTHREAD = 0x00000004
|
119
|
+
TH32CS_SNAPMODULE = 0x00000008
|
120
|
+
ERROR_NO_MORE_FILES = 0x00000018
|
113
121
|
end
|
@@ -24,6 +24,7 @@ module Process::Functions
|
|
24
24
|
ffi_lib :kernel32
|
25
25
|
|
26
26
|
attach_pfunc :CloseHandle, [:handle], :bool
|
27
|
+
attach_pfunc :CreateToolhelp32Snapshot, [:dword, :dword], :handle
|
27
28
|
attach_pfunc :GenerateConsoleCtrlEvent, [:dword, :dword], :bool
|
28
29
|
attach_pfunc :GetCurrentProcess, [], :handle
|
29
30
|
attach_pfunc :GetModuleHandle, :GetModuleHandleA, [:string], :hmodule
|
@@ -31,12 +32,22 @@ module Process::Functions
|
|
31
32
|
attach_pfunc :GetPriorityClass, [:handle], :dword
|
32
33
|
attach_pfunc :GetProcAddress, [:hmodule, :string], :pointer
|
33
34
|
attach_pfunc :GetVersionExA, [:pointer], :bool
|
35
|
+
attach_pfunc :Heap32ListFirst, [:handle, :pointer], :bool
|
36
|
+
attach_pfunc :Heap32ListNext, [:handle, :pointer], :bool
|
37
|
+
attach_pfunc :Heap32First, [:pointer, :dword, :uintptr_t], :bool
|
38
|
+
attach_pfunc :Heap32Next, [:pointer], :bool
|
39
|
+
attach_pfunc :Module32First, [:handle, :pointer], :bool
|
40
|
+
attach_pfunc :Module32Next, [:handle, :pointer], :bool
|
34
41
|
attach_pfunc :IsProcessInJob, [:handle, :pointer, :pointer], :bool # 2nd arg optional
|
35
42
|
attach_pfunc :OpenProcess, [:dword, :bool, :dword], :handle
|
43
|
+
attach_pfunc :Process32First, [:handle, :pointer], :bool
|
44
|
+
attach_pfunc :Process32Next, [:handle, :pointer], :bool
|
36
45
|
attach_pfunc :SetHandleInformation, [:handle, :dword, :dword], :bool
|
37
46
|
attach_pfunc :SetErrorMode, [:uint], :uint
|
38
47
|
attach_pfunc :SetPriorityClass, [:handle, :dword], :bool
|
39
48
|
attach_pfunc :TerminateProcess, [:handle, :uint], :bool
|
49
|
+
attach_pfunc :Thread32First, [:handle, :pointer], :bool
|
50
|
+
attach_pfunc :Thread32Next, [:handle, :pointer], :bool
|
40
51
|
attach_pfunc :WaitForSingleObject, [:handle, :dword], :dword
|
41
52
|
|
42
53
|
attach_pfunc :CreateRemoteThread,
|
data/lib/win32/process/helper.rb
CHANGED
@@ -108,11 +108,111 @@ module Process::Structs
|
|
108
108
|
)
|
109
109
|
end
|
110
110
|
|
111
|
+
class THREADENTRY32 < FFI::Struct
|
112
|
+
layout(
|
113
|
+
:dwSize, :dword,
|
114
|
+
:cntUsage, :dword,
|
115
|
+
:th32ThreadID, :dword,
|
116
|
+
:th32OwnerProcessID, :dword,
|
117
|
+
:tpBasePri, :long,
|
118
|
+
:tpDeltaPri, :long,
|
119
|
+
:dwFlags, :dword
|
120
|
+
)
|
121
|
+
end
|
122
|
+
|
123
|
+
class HEAPLIST32 < FFI::Struct
|
124
|
+
layout(
|
125
|
+
:dwSize, :size_t,
|
126
|
+
:th32ProcessID, :dword,
|
127
|
+
:th32HeapID, :uintptr_t,
|
128
|
+
:dwFlags, :dword
|
129
|
+
)
|
130
|
+
end
|
131
|
+
|
132
|
+
class HEAPENTRY32 < FFI::Struct
|
133
|
+
layout(
|
134
|
+
:dwSize, :size_t,
|
135
|
+
:hHandle, :handle,
|
136
|
+
:dwAddress, :uintptr_t,
|
137
|
+
:dwBlockSize, :size_t,
|
138
|
+
:dwFlags, :dword,
|
139
|
+
:dwLockCount, :dword,
|
140
|
+
:dwResvd, :dword,
|
141
|
+
:th32ProcessID, :dword,
|
142
|
+
:th32HeapID, :uintptr_t
|
143
|
+
)
|
144
|
+
end
|
145
|
+
|
146
|
+
class MODULEENTRY32 < FFI::Struct
|
147
|
+
layout(
|
148
|
+
:dwSize, :dword,
|
149
|
+
:th32ModuleID, :dword,
|
150
|
+
:th32ProcessID, :dword,
|
151
|
+
:GlblcntUsage, :dword,
|
152
|
+
:ProccntUsage, :dword,
|
153
|
+
:modBaseAddr, :pointer,
|
154
|
+
:modBaseSize, :dword,
|
155
|
+
:hModule, :handle,
|
156
|
+
:szModule, [:char, 256],
|
157
|
+
:szExePath, [:char, 260]
|
158
|
+
)
|
159
|
+
end
|
160
|
+
|
161
|
+
class PROCESSENTRY32 < FFI::Struct
|
162
|
+
layout(
|
163
|
+
:dwSize, :dword,
|
164
|
+
:cntUsage, :dword,
|
165
|
+
:th32ProcessID, :dword,
|
166
|
+
:th32DefaultHeapID, :uintptr_t,
|
167
|
+
:th32ModuleID, :dword,
|
168
|
+
:cntThreads, :dword,
|
169
|
+
:th32ParentProcessID, :dword,
|
170
|
+
:pcPriClassBase, :long,
|
171
|
+
:dwFlags, :dword,
|
172
|
+
:szExeFile, [:char, 260]
|
173
|
+
)
|
174
|
+
end
|
175
|
+
|
111
176
|
# Used by Process.create
|
177
|
+
|
112
178
|
ProcessInfo = Struct.new("ProcessInfo",
|
113
179
|
:process_handle,
|
114
180
|
:thread_handle,
|
115
181
|
:process_id,
|
116
182
|
:thread_id
|
117
183
|
)
|
184
|
+
|
185
|
+
# Used by Process.snapshot
|
186
|
+
|
187
|
+
ThreadSnapInfo = Struct.new("ThreadSnapInfo",
|
188
|
+
:thread_id,
|
189
|
+
:process_id,
|
190
|
+
:base_priority
|
191
|
+
)
|
192
|
+
|
193
|
+
HeapSnapInfo = Struct.new("HeapSnapInfo",
|
194
|
+
:address,
|
195
|
+
:block_size,
|
196
|
+
:flags,
|
197
|
+
:process_id,
|
198
|
+
:heap_id
|
199
|
+
)
|
200
|
+
|
201
|
+
ModuleSnapInfo = Struct.new("ModuleSnapInfo",
|
202
|
+
:process_id,
|
203
|
+
:address,
|
204
|
+
:module_size,
|
205
|
+
:handle,
|
206
|
+
:name,
|
207
|
+
:path
|
208
|
+
)
|
209
|
+
|
210
|
+
ProcessSnapInfo = Struct.new("ProcessSnapInfo",
|
211
|
+
:process_id,
|
212
|
+
:threads,
|
213
|
+
:parent_process_id,
|
214
|
+
:priority,
|
215
|
+
:flags,
|
216
|
+
:path
|
217
|
+
)
|
118
218
|
end
|
data/test/test_win32_process.rb
CHANGED
@@ -25,7 +25,7 @@ class TC_Win32Process < Test::Unit::TestCase
|
|
25
25
|
end
|
26
26
|
|
27
27
|
test "win32-process version is set to the correct value" do
|
28
|
-
assert_equal('0.
|
28
|
+
assert_equal('0.8.0', Process::WIN32_PROCESS_VERSION)
|
29
29
|
end
|
30
30
|
|
31
31
|
test "create basic functionality" do
|
@@ -298,6 +298,23 @@ class TC_Win32Process < Test::Unit::TestCase
|
|
298
298
|
assert_not_respond_to(Process, :volume_type)
|
299
299
|
end
|
300
300
|
|
301
|
+
test "snapshot method basic functionality" do
|
302
|
+
assert_respond_to(Process, :snapshot)
|
303
|
+
assert_nothing_raised{ Process.snapshot }
|
304
|
+
assert_kind_of(Hash, Process.snapshot)
|
305
|
+
end
|
306
|
+
|
307
|
+
test "snapshot accepts :thread, :module or :heap arguments" do
|
308
|
+
assert_nothing_raised{ Process.snapshot(:thread) }
|
309
|
+
assert_nothing_raised{ Process.snapshot(:module) }
|
310
|
+
assert_nothing_raised{ Process.snapshot(:heap) }
|
311
|
+
assert_nothing_raised{ Process.snapshot(:process) }
|
312
|
+
end
|
313
|
+
|
314
|
+
test "snapshot raises an error if an invalid argument is passed" do
|
315
|
+
assert_raise(ArgumentError){ Process.snapshot(:bogus) }
|
316
|
+
end
|
317
|
+
|
301
318
|
test "ffi functions are private" do
|
302
319
|
assert_not_respond_to(Process, :CloseHandle)
|
303
320
|
assert_not_respond_to(Process, :GetCurrentProcess)
|
data/win32-process.gemspec
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: win32-process
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Daniel Berger
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2015-
|
12
|
+
date: 2015-04-29 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: ffi
|