module Process

def create(args)


Process.spawn method instead of Process.create where possible.
If you really to use Process.wait, then you should use the

sleep 0.1 while !Process.get_exitcode(info.process_id)

To simulate Process.wait you can use this approach:

executable file in the security context of the specified credentials.
If the 'with_logon' option is set, then the process runs the specified

before the ProcessInfo struct is returned.
process_handle and the thread_handle are automatically closed for you
If the 'close_handles' option is set to true (the default) then the

* thread_id - Thread ID.
* process_id - Process ID.
* thread_handle - The handle to the primary thread of the process.
* process_handle - The handle to the newly created process.

The ProcessInfo struct contains the following members:

automatically OR'd to the +startf_flags+ value.
is automatically set to true and the Process::STARTF_USESTDHANDLES flag is
If 'stdin', 'stdout' or 'stderr' are specified, then the +inherit+ value

an option for JRuby.
are not currently supported. Unfortunately, setting these is not currently
IO objects or file descriptors (i.e. a fileno). However, StringIO objects
Note that the 'stdin', 'stdout' and 'stderr' options can be either Ruby

* stderr
* stdout
* stdin
* startf_flags
* sw_flags
* fill_attribute
* y_count_chars
* x_count_chars
* y_size
* x_size
* y
* x
* title
* desktop

and the StartupInfo struct on MSDN for more information.
GUI or console processes. See the documentation on CreateProcess()
part of the StartupInfo struct, and are generally only meaningful for
The startup_info key takes a hash. Its keys are attributes that are

mandatory.
of 'with_logon'. If 'with_logon' is set, then the 'password' option is
The 'domain' and 'password' options are only relevent in the context

require an explicit path or extension to work.
be preferred if only one of them is set because it does not (necessarily)
error is raised. Both may be set individually, but 'command_line' should
Of these, the 'command_line' or 'app_name' must be specified or an

* password (default: nil, mandatory if with_logon)
* domain (default: nil)
* with_logon (default: nil)
* close_handles (default: true)
* environment (default: nil)
* startup_info (default: nil)
* cwd (default: Dir.pwd)
* creation_flags (default: 0)
* thread_inherit (default: false)
* process_inherit (default: false)
* inherit (default: false)
* app_name (default: nil)
* command_line (this or app_name must be present)

There are several primary keys:
returning a ProcessInfo struct. It accepts a hash as an argument.
This is a wrapper for the CreateProcess() function. It executes a process,

Process.create(key => value, ...) => ProcessInfo
def create(args)
  unless args.is_a?(Hash)
    raise TypeError, "hash keyword arguments expected"
  end
  valid_keys = %w{
    app_name command_line inherit creation_flags cwd environment
    startup_info thread_inherit process_inherit close_handles with_logon
    domain password
  }
  valid_si_keys = %w{
    startf_flags desktop title x y x_size y_size x_count_chars
    y_count_chars fill_attribute sw_flags stdin stdout stderr
  }
  # Set default values
  hash = {
    "app_name"       => nil,
    "creation_flags" => 0,
    "close_handles"  => true,
  }
  # Validate the keys, and convert symbols and case to lowercase strings.
  args.each { |key, val|
    key = key.to_s.downcase
    unless valid_keys.include?(key)
      raise ArgumentError, "invalid key '#{key}'"
    end
    hash[key] = val
  }
  si_hash = {}
  # If the startup_info key is present, validate its subkeys
  if hash["startup_info"]
    hash["startup_info"].each { |key, val|
      key = key.to_s.downcase
      unless valid_si_keys.include?(key)
        raise ArgumentError, "invalid startup_info key '#{key}'"
      end
      si_hash[key] = val
    }
  end
  # The +command_line+ key is mandatory unless the +app_name+ key
  # is specified.
  unless hash["command_line"]
    if hash["app_name"]
      hash["command_line"] = hash["app_name"]
      hash["app_name"] = nil
    else
      raise ArgumentError, "command_line or app_name must be specified"
    end
  end
  env = nil
  # The env string should be passed as a string of ';' separated paths.
  if hash["environment"]
    env = hash["environment"]
    unless env.respond_to?(:join)
      env = hash["environment"].split(File::PATH_SEPARATOR)
    end
    env = env.map { |e| e + 0.chr }.join("") + 0.chr
    env.to_wide_string! if hash["with_logon"]
  end
  # Process SECURITY_ATTRIBUTE structure
  process_security = nil
  if hash["process_inherit"]
    process_security = SECURITY_ATTRIBUTES.new
    process_security[:nLength] = 12
    process_security[:bInheritHandle] = 1
  end
  # Thread SECURITY_ATTRIBUTE structure
  thread_security = nil
  if hash["thread_inherit"]
    thread_security = SECURITY_ATTRIBUTES.new
    thread_security[:nLength] = 12
    thread_security[:bInheritHandle] = 1
  end
  # Automatically handle stdin, stdout and stderr as either IO objects
  # or file descriptors. This won't work for StringIO, however. It also
  # will not work on JRuby because of the way it handles internal file
  # descriptors.
  #
  %w{stdin stdout stderr}.each { |io|
    if si_hash[io]
      if si_hash[io].respond_to?(:fileno)
        handle = get_osfhandle(si_hash[io].fileno)
      else
        handle = get_osfhandle(si_hash[io])
      end
      if handle == INVALID_HANDLE_VALUE
        ptr = FFI::MemoryPointer.new(:int)
        if windows_version >= 6 && get_errno(ptr) == 0
          errno = ptr.read_int
        else
          errno = FFI.errno
        end
        raise SystemCallError.new("get_osfhandle", errno)
      end
      # Most implementations of Ruby on Windows create inheritable
      # handles by default, but some do not. RF bug #26988.
      bool = SetHandleInformation(
        handle,
        HANDLE_FLAG_INHERIT,
        HANDLE_FLAG_INHERIT
      )
      raise SystemCallError.new("SetHandleInformation", FFI.errno) unless bool
      si_hash[io] = handle
      si_hash["startf_flags"] ||= 0
      si_hash["startf_flags"] |= STARTF_USESTDHANDLES
      hash["inherit"] = true
    end
  }
  procinfo  = PROCESS_INFORMATION.new
  startinfo = STARTUPINFO.new
  unless si_hash.empty?
    startinfo[:cb]              = startinfo.size
    startinfo[:lpDesktop]       = si_hash["desktop"] if si_hash["desktop"]
    startinfo[:lpTitle]         = si_hash["title"] if si_hash["title"]
    startinfo[:dwX]             = si_hash["x"] if si_hash["x"]
    startinfo[:dwY]             = si_hash["y"] if si_hash["y"]
    startinfo[:dwXSize]         = si_hash["x_size"] if si_hash["x_size"]
    startinfo[:dwYSize]         = si_hash["y_size"] if si_hash["y_size"]
    startinfo[:dwXCountChars]   = si_hash["x_count_chars"] if si_hash["x_count_chars"]
    startinfo[:dwYCountChars]   = si_hash["y_count_chars"] if si_hash["y_count_chars"]
    startinfo[:dwFillAttribute] = si_hash["fill_attribute"] if si_hash["fill_attribute"]
    startinfo[:dwFlags]         = si_hash["startf_flags"] if si_hash["startf_flags"]
    startinfo[:wShowWindow]     = si_hash["sw_flags"] if si_hash["sw_flags"]
    startinfo[:cbReserved2]     = 0
    startinfo[:hStdInput]       = si_hash["stdin"] if si_hash["stdin"]
    startinfo[:hStdOutput]      = si_hash["stdout"] if si_hash["stdout"]
    startinfo[:hStdError]       = si_hash["stderr"] if si_hash["stderr"]
  end
  app = nil
  cmd = nil
  # Convert strings to wide character strings if present
  if hash["app_name"]
    app = hash["app_name"].to_wide_string
  end
  if hash["command_line"]
    cmd = hash["command_line"].to_wide_string
  end
  if hash["cwd"]
    cwd = hash["cwd"].to_wide_string
  end
  if hash["with_logon"]
    logon = hash["with_logon"].to_wide_string
    if hash["password"]
      passwd = hash["password"].to_wide_string
    else
      raise ArgumentError, "password must be specified if with_logon is used"
    end
    if hash["domain"]
      domain = hash["domain"].to_wide_string
    end
    hash["creation_flags"] |= CREATE_UNICODE_ENVIRONMENT
    bool = CreateProcessWithLogonW(
      logon,                  # User
      domain,                 # Domain
      passwd,                 # Password
      LOGON_WITH_PROFILE,     # Logon flags
      app,                    # App name
      cmd,                    # Command line
      hash["creation_flags"], # Creation flags
      env,                    # Environment
      cwd,                    # Working directory
      startinfo,              # Startup Info
      procinfo                # Process Info
    )
    unless bool
      raise SystemCallError.new("CreateProcessWithLogonW", FFI.errno)
    end
  else
    inherit = hash["inherit"] ? 1 : 0
    bool = CreateProcessW(
      app,                    # App name
      cmd,                    # Command line
      process_security,       # Process attributes
      thread_security,        # Thread attributes
      inherit,                # Inherit handles?
      hash["creation_flags"], # Creation flags
      env,                    # Environment
      cwd,                    # Working directory
      startinfo,              # Startup Info
      procinfo                # Process Info
    )
    unless bool
      raise SystemCallError.new("CreateProcess", FFI.errno)
    end
  end
  # Automatically close the process and thread handles in the
  # PROCESS_INFORMATION struct unless explicitly told not to.
  if hash["close_handles"]
    CloseHandle(procinfo[:hProcess])
    CloseHandle(procinfo[:hThread])
  end
  ProcessInfo.new(
    procinfo[:hProcess],
    procinfo[:hThread],
    procinfo[:dwProcessId],
    procinfo[:dwThreadId]
  )
end