Ruby から Win32 API 叩くためのクラス
添付ライブラリに含まれる
ちょっと汚いけどこんな感じで行けそう
例えば上記のスクリプトを PSAPItest.rb として保存しておくと以下のようにして使える
ポインタ渡しの引数に戻り値が入る場合とかをどう扱うかが悩みどころな気がする
wrapper に徹するべきか、Ruby らしい関数やクラスに仕立てるべきか悩ましい
Ruby から sizeof() の値知る方法がないのでちょっと困る。
例えば
GetModuleFileNameEx() の
nSize って sizeof(lpFilename)/sizeof(TCHAR) のはずなんだけど
sizeof(TCHAR) とかどうやってとれば良いんだ?
32bit 環境と 64bit 環境でポインタサイズが違うのでハマりそうな予感
とりあえず環境の判別は以下の方法で可能な模様
Answers: Determining XP 32 or 64Bit by calling IsWow64Process():
添付ライブラリに含まれる
#!/usr/bin/ruby require 'Win32API' class String def to_DWORD x = 0 self.to_DWORDS[x] end def to_DWORDS self.unpack("L*") end end class Win32API SIZEOF_DWORD = 4 SIZEOF_TCHAR = 1 # @reference winnt.h SYNCHRONIZE = 0x100000 STANDARD_RIGHTS_REQUIRED = 0xF0000 PROCESS_TERMINATE = 1 PROCESS_CREATE_THREAD = 2 PROCESS_SET_SESSIONID = 4 PROCESS_VM_OPERATION = 8 PROCESS_VM_READ = 16 PROCESS_VM_WRITE = 32 PROCESS_DUP_HANDLE = 64 PROCESS_CREATE_PROCESS = 128 PROCESS_SET_QUOTA = 256 PROCESS_SUSPEND_RESUME = 0x0800 PROCESS_SET_INFORMATION = 512 PROCESS_QUERY_INFORMATION = 1024 PROCESS_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xFFF) ############################################################################ # kernel32.dll # @reference http://msdn.microsoft.com/ja-jp/library/cc429278.aspx def Win32API.OpenProcess(dwDesiredAccess, bInheritHandle, dwProcessId) openprocess = Win32API.new('kernel32.dll', 'OpenProcess', 'lll', 'l') bInheritHandle = bInheritHandle ? 1 : 0 hProcess = openprocess.call(dwDesiredAccess, bInheritHandle, dwProcessId) end # @reference http://msdn.microsoft.com/ja-jp/library/cc429605.aspx def Win32API.CloseHandle(hObject) closehandle = Win32API.new('kernel32.dll', 'CloseHandle', 'l', 'l') closehandle.call(hObject) ? true : false end # @reference http://msdn.microsoft.com/ja-jp/library/cc429376.aspx def Win32API.TerminateProcess(hProcess, uExitCode) terminateprocess = Win32API.new('kernel32.dll', 'TerminateProcess', 'll', 'l') terminateprocess.call(hProcess, uExitCode) ? true : false end ############################################################################ # psapi.dll # @reference http://msdn.microsoft.com/ja-jp/library/cc429383.aspx def Win32API.EnumProcesses enumprocesses = Win32API.new('psapi.dll', 'EnumProcesses', 'plp', 'i') lpidProcess = [0].pack("L") * 1024 cbNeeded = [0].pack("L") while true enumprocesses.call(lpidProcess, lpidProcess.length, cbNeeded) break if cbNeeded.to_DWORD < lpidProcess.length lpidProcess *= 2 end lpidProcess.slice(0, cbNeeded.to_DWORD).to_DWORDS end # @reference http://msdn.microsoft.com/ja-jp/library/cc429387.aspx def Win32API.EnumProcessModules(hProcess) enumprocessmodules = Win32API.new('psapi.dll', 'EnumProcessModules', 'lplp', 'l') lphModule = "" lpcbNeeded = [0].pack("L") while true do result = enumprocessmodules.call(hProcess, lphModule, lphModule.length, lpcbNeeded) return [] if result == 0 break if lpcbNeeded.to_DWORD <= lphModule.length lphModule = "\0" * lpcbNeeded.to_DWORD end lphModule.slice(0, lpcbNeeded.to_DWORD).to_DWORDS end # @reference http://msdn.microsoft.com/ja-jp/library/cc429400.aspx def Win32API.GetModuleBaseName(hProcess, hModule) getmodulebasename = Win32API.new("psapi.dll", "GetModuleBaseName", "llpl", "l") lpBaseName = "\0" * 1024 while true do len = getmodulebasename.call(hProcess, hModule, lpBaseName, lpBaseName.length / SIZEOF_TCHAR) break if len < lpBaseName.length / SIZEOF_TCHAR lpBaseName *= 2 end lpBaseName.slice(0, len * SIZEOF_TCHAR) end # @reference http://msdn.microsoft.com/ja-jp/library/cc429403.aspx def Win32API.GetModuleFileNameEx(hProcess, hModule) getmodulefilenameex = Win32API.new("psapi.dll", "GetModuleFileNameEx", "llpl", "l") lpFilename = "\0" * 1024 while true do len = getmodulefilenameex.call(hProcess, hModule, lpFilename, lpFilename.length / SIZEOF_TCHAR) break if len < lpFilename.length / SIZEOF_TCHAR lpFilename *= 2 end lpFilename.slice(0, len * SIZEOF_TCHAR) end end def putProcessList bShowModules = false Win32API.EnumProcesses.sort.each do |pidProcess| hProcess = Win32API.OpenProcess(Win32API::PROCESS_QUERY_INFORMATION | Win32API::PROCESS_VM_READ, FALSE, pidProcess) hModules = Win32API.EnumProcessModules(hProcess) indent = sprintf("%d ", pidProcess) hModules.each do |hModule| printf("%s%s\n", indent, Win32API.GetModuleFileNameEx(hProcess, hModule)) indent = "\t" break if !bShowModules end Win32API.CloseHandle hProcess end end def KillProcess pid hProcess = Win32API.OpenProcess(Win32API::PROCESS_TERMINATE, FALSE, pid) Win32API.TerminateProcess hProcess, 0 Win32API.CloseHandle hProcess end
ちょっと汚いけどこんな感じで行けそう
例えば上記のスクリプトを PSAPItest.rb として保存しておくと以下のようにして使える
$ ruby -r PSAPItest.rb -e "putProcessList"kill したいプロセスの pid が 12345 なら以下のようにすれば kill できる
$ ruby -r PSAPItest.rb -e "KillProcess 12345"
ポインタ渡しの引数に戻り値が入る場合とかをどう扱うかが悩みどころな気がする
wrapper に徹するべきか、Ruby らしい関数やクラスに仕立てるべきか悩ましい
Ruby から sizeof() の値知る方法がないのでちょっと困る。
例えば
GetModuleFileNameEx() の
nSize って sizeof(lpFilename)/sizeof(TCHAR) のはずなんだけど
sizeof(TCHAR) とかどうやってとれば良いんだ?
32bit 環境と 64bit 環境でポインタサイズが違うのでハマりそうな予感
とりあえず環境の判別は以下の方法で可能な模様
Answers: Determining XP 32 or 64Bit by calling IsWow64Process():
タグ
コメントをかく