module ChildProcess::Windows::Lib

Public Class Methods

create_proc(cmd, opts = {}) click to toggle source
# File lib/childprocess/windows/functions.rb, line 5
def self.create_proc(cmd, opts = {})
  cmd_ptr = FFI::MemoryPointer.from_string cmd

  flags   = 0
  inherit = !!opts[:inherit]

  flags |= DETACHED_PROCESS if opts[:detach]

  si = StartupInfo.new
  pi = ProcessInfo.new

  if opts[:stdout] || opts[:stderr]
    si[:dwFlags] ||= 0
    si[:dwFlags] |= STARTF_USESTDHANDLES
    inherit = true

    si[:hStdOutput] = handle_for(opts[:stdout].fileno) if opts[:stdout]
    si[:hStdError]  = handle_for(opts[:stderr].fileno) if opts[:stderr]
  end

  if opts[:duplex]
    read_pipe_ptr  = FFI::MemoryPointer.new(:pointer)
    write_pipe_ptr = FFI::MemoryPointer.new(:pointer)
    sa         = SecurityAttributes.new(:inherit => true)

    ok = create_pipe(read_pipe_ptr, write_pipe_ptr, sa, 0)
    ok or raise Error, last_error_message

    read_pipe = read_pipe_ptr.read_pointer
    write_pipe = write_pipe_ptr.read_pointer

    si[:hStdInput] = read_pipe
  end

  ok = create_process(nil, cmd_ptr, nil, nil, inherit, flags, nil, nil, si, pi)
  ok or raise Error, last_error_message

  close_handle pi[:hProcess]
  close_handle pi[:hThread]

  if opts[:duplex]
    opts[:stdin] = io_for(duplicate_handle(write_pipe), File::WRONLY)
    close_handle read_pipe
    close_handle write_pipe
  end

  pi[:dwProcessId]
end
duplicate_handle(handle) click to toggle source
# File lib/childprocess/windows/functions.rb, line 102
def self.duplicate_handle(handle)
  dup  = FFI::MemoryPointer.new(:pointer)
  proc = current_process

  ok = _duplicate_handle(
    proc, handle, proc, dup, 0, false, DUPLICATE_SAME_ACCESS)

  ok or raise Error, last_error_message

  dup.read_pointer
ensure
  close_handle proc
end
handle_for(fd_or_io) click to toggle source
# File lib/childprocess/windows/functions.rb, line 66
def self.handle_for(fd_or_io)
  case fd_or_io
  when IO
    handle = get_osfhandle(fd.fileno)
  when Fixnum
    handle = get_osfhandle(fd_or_io)
  else
    if fd_or_io.respond_to?(:to_io)
      io = fd_or_io.to_io

      unless io.kind_of?(IO)
        raise TypeError, "expected #to_io to return an instance of IO"
      end

      handle = get_osfhandle(io.fileno)
    else
      raise TypeError, "invalid type: #{fd_or_io.inspect}"
    end
  end

  if handle == INVALID_HANDLE_VALUE
    raise Error, last_error_message
  end

  handle
end
io_for(handle, flags = File::RDONLY) click to toggle source
# File lib/childprocess/windows/functions.rb, line 93
def self.io_for(handle, flags = File::RDONLY)
  fd = open_osfhandle(handle, flags)
  if fd == -1
    raise Error, last_error_message
  end

  ::IO.for_fd fd, flags
end
last_error_message() click to toggle source
# File lib/childprocess/windows/functions.rb, line 54
def self.last_error_message
  errnum = get_last_error
  buf = FFI::MemoryPointer.new :char, 512

  size = format_message(
    FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ARGUMENT_ARRAY,
    nil, errnum, 0, buf, buf.size, nil
  )

  buf.read_string(size).strip
end
msvcrt_name() click to toggle source
# File lib/childprocess/windows.rb, line 9
def self.msvcrt_name
  host_part = RbConfig::CONFIG['host_os'].split("_")[1]
  manifest  = File.join(RbConfig::CONFIG['bindir'], 'ruby.exe.manifest')

  if host_part && host_part.to_i > 80 && File.exists?(manifest)
    "msvcr#{host_part}"
  else
    "msvcrt"
  end
end