Skip to content

Instantly share code, notes, and snippets.

Created December 29, 2008 22:50
Show Gist options
  • Save anonymous/41421 to your computer and use it in GitHub Desktop.
Save anonymous/41421 to your computer and use it in GitHub Desktop.
require "Win32API"
module Windows
module Processes
EnumProcesses = Win32API.new("psapi","EnumProcesses",['P','L','P'],'L')
OpenProcess = Win32API.new("kernel32","OpenProcess",['L','L','L'],'L')
CloseHandle = Win32API.new("kernel32","CloseHandle",['L'],'L')
EnumProcessModules = Win32API.new("psapi","EnumProcessModules",['L','P','L','P'],'L')
GetModuleBaseName = Win32API.new("psapi","GetModuleBaseName",['L','L','P','L'],'L')
GetModuleFileNameEx = Win32API.new("psapi","GetModuleFileNameEx",['L','L','P','L'],'L')
GetProcessMemoryInfo = Win32API.new("psapi","GetProcessMemoryInfo",['L','P','L'],'L')
PROC_QUERY_INFO = 0x0400
PROC_VM_READ = 0x0010
MAX_PATH = 256
ProcTable = Struct.new("ProcTableStruct",
:pid,
:comm,
:cmdline,
:path,
:page_fault_count,
:peak_working_set_size,
:working_set_size,
:quota_peak_paged_pool_usage,
:quota_peak_non_paged_pool_usage,
:quota_paged_pool_usage,
:quota_non_paged_pool_usage,
:page_file_usage,
:peak_page_file_usage
)
def self.fields
if block_given?
ProcTable.members.each{|m| yield m}
else
ProcTable.members
end
end
# For when there's a process HANDLE but no EnumProcesses information
def self.get_partial_process_info_struct(pid)
ProcTable.new(pid,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil)
end
def self.get_process_info_struct(pid)
proc_name = "unknown"
h_process = OpenProcess.Call(PROC_QUERY_INFO | PROC_VM_READ,0,pid)
if h_process == 0
return get_partial_process_info_struct(pid)
else
h_mod = "\0" * 1024 * 4
cb_needed = "\0" * 4
if EnumProcessModules.Call(h_process, h_mod, 1024, cb_needed) != 0
cb_needed = cb_needed.unpack('L')[0]
modules = h_mod.unpack('L*')[0,cb_needed / 4]
mod = modules[0]
proc_name = "\0" * MAX_PATH
GetModuleBaseName.Call(h_process,mod,proc_name,MAX_PATH)
proc_name.gsub!("\0","")
cmd_line = nil
path = nil
modules.each{ |mod|
mod_name = "\0" * MAX_PATH
GetModuleFileNameEx.Call(h_process,mod,mod_name,MAX_PATH)
mod_name.gsub!("\0","")
cmd_line = mod_name
# Handle dirname bug for Windows
path = File.dirname(mod_name.gsub(/\\/,'/')).gsub(/\//,'\\')
break
}
end
pmc = "\0" * 10 * 4
if GetProcessMemoryInfo.Call( h_process, pmc, 40) != 0
pmc = pmc.unpack('LLLLLLLLLL')[1,9]
end
end
CloseHandle.Call(h_process)
ProcTable.new(pid,proc_name,cmd_line,path,*pmc)
end
def self.ps(arg=nil)
unless arg.nil?
raise TypeError unless arg.kind_of?(Fixnum)
end
aProcesses = "\0" * 1024 * 4
cbNeeded = "\0" * 4
r = EnumProcesses.Call(aProcesses,1024,cbNeeded);
cbNeeded = cbNeeded.unpack('L')[0]
cProcesses = cbNeeded / 4
aProcesses = aProcesses.unpack('L*')[0,cProcesses]
structs = []
aProcesses.each{ |pid|
if arg
next unless pid == arg
end
if block_given?
ps = Win32Process.new(get_process_info_struct(pid))
structs << ps if yield ps
else
structs << Win32Process.new(get_process_info_struct(pid))
end
}
return structs.first if arg
return structs
end
def self.kill(pid)
Process.kill
end
private_class_method :get_process_info_struct
private_class_method :get_partial_process_info_struct
class Win32Process
attr_reader :struct
def initialize(struct)
@struct = struct
@struct.each_pair { |name, value|
if [:comm, :cmdline, :path].include?(name)
@struct[name] = "" if value.nil?
else
@struct[name] = 0 if value.nil?
end
}
end
def to_s
"<Win32Process\n"+
" pid="+@struct["pid"].to_s+"\n"+
" comm="+@struct["comm"].to_s+"\n"+
" cmdline="+@struct["cmdline"].to_s+"\n"+
" path="+@struct["path"].to_s+"\n"+
" page_fault_count="+@struct["page_fault_count"].to_s+"\n"+
" peak_working_set_size="+@struct["peak_working_set_size"].to_s+"\n"+
" working_set_size="+@struct["working_set_size"].to_s+"\n"+
" quota_peak_paged_pool_usage="+@struct["quota_peak_paged_pool_usage"].to_s+"\n"+
" quota_peak_non_paged_pool_usage="+@struct["quota_peak_non_paged_pool_usage"].to_s+"\n"+
" quota_paged_pool_usage="+@struct["quota_paged_pool_usage"].to_s+"\n"+
" quota_non_paged_pool_usage="+@struct["quota_non_paged_pool_usage"].to_s+"\n"+
" page_file_usage="+@struct["page_file_usage"].to_s+"\n"+
" peak_page_file_usage="+@struct["peak_page_file_usage"].to_s+"\n"+
">\n"
end
def kill(code=9)
Process.kill(code, self.pid)
end
def method_missing(methId, *args)
begin
eval("@struct[\"#{methId.id2name}\"]")
rescue Exception => e
error "Method '#{methId.id2name}' does not exist for #{self.class}!"
end
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment