-
-
Save rdp/8229520 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require "rubygems" | |
require "ffi" | |
module WinProcess | |
extend FFI::Library | |
ffi_lib "kernel32" | |
ffi_convention :stdcall | |
class Error < StandardError | |
end | |
# typedef struct _STARTUPINFO { | |
# DWORD cb; | |
# LPTSTR lpReserved; | |
# LPTSTR lpDesktop; | |
# LPTSTR lpTitle; | |
# DWORD dwX; | |
# DWORD dwY; | |
# DWORD dwXSize; | |
# DWORD dwYSize; | |
# DWORD dwXCountChars; | |
# DWORD dwYCountChars; | |
# DWORD dwFillAttribute; | |
# DWORD dwFlags; | |
# WORD wShowWindow; | |
# WORD cbReserved2; | |
# LPBYTE lpReserved2; | |
# HANDLE hStdInput; | |
# HANDLE hStdOutput; | |
# HANDLE hStdError; | |
# } STARTUPINFO, *LPSTARTUPINFO; | |
class StartupInfo < FFI::Struct | |
layout :cb, :ulong, | |
:lpReserved, :pointer, | |
:lpDesktop, :pointer, | |
:lpTitle, :pointer, | |
:dwX, :ulong, | |
:dwY, :ulong, | |
:dwXSize, :ulong, | |
:dwYSize, :ulong, | |
:dwXCountChars, :ulong, | |
:dwYCountChars, :ulong, | |
:dwFillAttribute, :ulong, | |
:dwFlags, :ulong, | |
:wShowWindow, :ushort, | |
:cbReserved2, :ushort, | |
:lpReserved2, :pointer, | |
:hStdInput, :pointer, # void ptr | |
:hStdOutput, :pointer, # void ptr | |
:hStdError, :pointer # void ptr | |
end | |
# typedef struct _PROCESS_INFORMATION { | |
# HANDLE hProcess; | |
# HANDLE hThread; | |
# DWORD dwProcessId; | |
# DWORD dwThreadId; | |
# } PROCESS_INFORMATION, *LPPROCESS_INFORMATION; | |
class ProcessInfo < FFI::Struct | |
layout :hProcess, :pointer, # void ptr | |
:hThread, :pointer, # void ptr | |
:dwProcessId, :uint, | |
:dwThreadId, :uint | |
end | |
# BOOL WINAPI CreateProcess( | |
# __in_opt LPCTSTR lpApplicationName, | |
# __inout_opt LPTSTR lpCommandLine, | |
# __in_opt LPSECURITY_ATTRIBUTES lpProcessAttributes, | |
# __in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes, | |
# __in BOOL bInheritHandles, | |
# __in DWORD dwCreationFlags, | |
# __in_opt LPVOID lpEnvironment, | |
# __in_opt LPCTSTR lpCurrentDirectory, | |
# __in LPSTARTUPINFO lpStartupInfo, | |
# __out LPPROCESS_INFORMATION lpProcessInformation | |
# ); | |
attach_function :create_process, :CreateProcessA, | |
[:pointer, :pointer, :pointer, :pointer, :bool, | |
:ulong, :pointer, :pointer, :pointer, :pointer], :bool | |
attach_function :get_last_error, :GetLastError, [], :ulong | |
# DWORD WINAPI WaitForSingleObject( | |
# _In_ HANDLE hHandle, | |
# _In_ DWORD dwMilliseconds | |
# ); | |
# TODO attach_function wait_for_single_object ... | |
# DWORD WINAPI FormatMessage( | |
# __in DWORD dwFlags, | |
# __in_opt LPCVOID lpSource, | |
# __in DWORD dwMessageId, | |
# __in DWORD dwLanguageId, | |
# __out LPTSTR lpBuffer, | |
# __in DWORD nSize, | |
# __in_opt va_list *Arguments | |
# ); | |
attach_function :format_message, :FormatMessageA, [:ulong, :pointer, :ulong, :ulong, | |
:pointer, :ulong, :pointer], :ulong | |
attach_function :close_handle, :CloseHandle, [:pointer], :bool | |
FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000 | |
FORMAT_MESSAGE_ARGUMENT_ARRAY = 0x00002000 | |
module_function | |
STARTF_USESHOWWINDOW = 0x00000001 | |
SW_HIDE = 0 | |
def create(cmd, opts = {}) | |
cmd_ptr = FFI::MemoryPointer.from_string cmd | |
si = StartupInfo.new | |
si[:dwFlags] |= STARTF_USESHOWWINDOW | |
si[:wShowWindow] |= SW_HIDE | |
pi = ProcessInfo.new | |
if create_process(nil, cmd_ptr, nil, nil, !!opts[:inherit], 0, nil, nil, si, pi) | |
close_handle pi[:hProcess] | |
close_handle pi[:hThread] | |
# to wait for termination, something like WaitForSingleObject(pi[:hProcess], INFINITE), since ruby's | |
pi[:dwProcessId] | |
else | |
raise Error, last_error_message | |
end | |
end | |
def wait_for_terminate pid | |
end | |
def last_error_message | |
errnum = get_last_error() | |
buf = FFI::MemoryPointer.new :char, 512 | |
flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ARGUMENT_ARRAY | |
size = format_message(flags, nil, errnum, 0, buf, buf.size, nil) | |
buf.read_string(size).strip | |
end | |
end | |
pid = WinProcess.create("curl www.google.com") # this doesn't use any shellword expansion, so no way to get output, request it in a comment! | |
begin | |
loop { Process.kill(0, pid); sleep 1; puts 'still alive'} | |
rescue Errno::ESRCH | |
# it's dead | |
end | |
puts 'done' |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment