-
-
Save jarib/280865 to your computer and use it in GitHub Desktop.
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, | |
: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, :ulong, | |
:dwThreadId, :ulong | |
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 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 | |
def create(cmd, opts = {}) | |
cmd_ptr = FFI::MemoryPointer.from_string cmd | |
si = StartupInfo.new | |
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] | |
pi[:dwProcessId] # returns the wrong pid?! | |
else | |
raise Error, last_error_message | |
end | |
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 | |
p :pid => WinProcess.create("C:\\Windows\\System32\\regedt32.exe") | |
Thanks. This code has been folded into my ChildProcess gem:
http://github.com/jarib/childprocess
It uses java.lang.ProcessBuilder on JRuby though, so may not do exactly what you want.
a #pid method in the childprocess gem might be kind :)
What would you use it for? It's basically impossible on JRuby the way it's currently implemented, since we use ProcessBuilder et al there.
as a follow-up, 1.6.0RC2 now returns the correct pid's for (non shell-looking commands) in windows, though this might still be useful as something of a Process.spawn for jruby windows 1.8 (and 1.9 until it actually works there--doesn't yet I don't think)
Is the StartupInfo struct missing dwFlags?
@dsjbirch: It's there in the gem. I'm not maintaining the code in this gist ;)
@jarib, that is very cool!
happiness. A process creator for jruby that actually returns the right PID. you rock.
NB that for me I couldn't run regedt32.exe, but running "ruby -e sleep" works well.