Skip to content

Instantly share code, notes, and snippets.

@jarib
Created January 19, 2010 11:13
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jarib/280865 to your computer and use it in GitHub Desktop.
Save jarib/280865 to your computer and use it in GitHub Desktop.
FFI wrapper for CreateProcess()
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")
@rdp
Copy link

rdp commented Oct 21, 2010

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.

>>* p :pid => WinProcess.create("C:\\Windows\\System32\\regedt32.exe")
WinProcess::Error: The operation completed successfully.
        from (irb):116:in `create'
        from (irb):134
>>*  p :pid => WinProcess.create("C:\\Windows\\System32\\regedt32.exe")
WinProcess::Error: The requested operation requires elevation.
        from (irb):116:in `create'
        from (irb):136

@jarib
Copy link
Author

jarib commented Oct 21, 2010

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.

@rdp
Copy link

rdp commented Jan 13, 2011

a #pid method in the childprocess gem might be kind :)

@jarib
Copy link
Author

jarib commented Jan 13, 2011

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.

@rdp
Copy link

rdp commented Feb 11, 2011

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)

@bluekeys
Copy link

Is the StartupInfo struct missing dwFlags?

http://msdn.microsoft.com/en-us/library/ms686331.aspx

@jarib
Copy link
Author

jarib commented Apr 18, 2012

@dsjbirch: It's there in the gem. I'm not maintaining the code in this gist ;)

@bluekeys
Copy link

@jarib, that is very cool!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment