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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7abeeac7e3b746666569ab363e702c8939a4a596
4
- data.tar.gz: 693447613255756de95668ddfefa702ecc5b9921
3
+ metadata.gz: 47640c7bc0c96cd8615348e2f8757c0e1aecec95
4
+ data.tar.gz: 3f391446c320d0bd0feac23f0effff0bc945ae65
5
5
  SHA512:
6
- metadata.gz: 2a7faa782828c7a8cbbf54f997ded9a3c97023b641417207e03938378520f0072b29e7fc1d11c26307e59c94ff975cdfdfebdbe9cbc0adedf2abe57a1251ecee
7
- data.tar.gz: 9a1ba82a14f16365850fbc7e50787c3233fedd3d54307757202dd60d05a9f4057cc4e617d3ad7d97783861c4098ee82c6bfdb56a8d4210d6404aca1a0682e613
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
@@ -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.7.5'
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,
@@ -10,4 +10,4 @@ class String
10
10
  def to_wide_string!
11
11
  replace((self + 0.chr).encode('UTF-16LE'))
12
12
  end
13
- end
13
+ end
@@ -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
@@ -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.7.5', Process::WIN32_PROCESS_VERSION)
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)
@@ -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.7.5'
5
+ spec.version = '0.8.0'
6
6
  spec.license = 'Artistic 2.0'
7
7
  spec.authors = ['Daniel Berger', 'Park Heesob']
8
8
  spec.email = 'djberg96@gmail.com'
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.7.5
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-03-03 00:00:00.000000000 Z
12
+ date: 2015-04-29 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: ffi