win32-process 0.7.5 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|