Class: Win32Open4::ProcessHandler

Inherits:
Object
  • Object
show all
Includes:
Kernel32
Defined in:
lib/procreate/win32/open4.rb

Overview

Responsible for launching a process, forwarding its stdout & stderr, and catching its error code. TODO: stdin is not currently handled in the ProcessHandler#relay method, though it should be fairly straightforward to add.

Constant Summary

Constants included from Kernel32

Kernel32::API, Kernel32::ERROR_SUCCESS, Kernel32::FORMAT_MESSAGE_ARGUMENT_ARRAY, Kernel32::FORMAT_MESSAGE_FROM_SYSTEM, Kernel32::HANDLE_FLAG_INHERIT, Kernel32::HANDLE_FLAG_PROTECT_FROM_CLOSE, Kernel32::NORMAL_PRIORITY_CLASS, Kernel32::PROCESS_INFO_SIZE, Kernel32::SECURITY_ATTRIBUTES_SIZE, Kernel32::STARTF_USESHOWWINDOW, Kernel32::STARTF_USESTDHANDLES, Kernel32::STARTUP_INFO_SIZE, Kernel32::WAIT_ABANDONED, Kernel32::WAIT_ABANDONED_0, Kernel32::WAIT_FAILED, Kernel32::WAIT_OBJECT_0, Kernel32::WAIT_TIMEOUT

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Kernel32

close_handle, create_pipe, create_process, peek_named_pipe, raise_last_error!, read_file, set_handle_information, write_file

Constructor Details

#initialize(cmdline, params = {}) ⇒ ProcessHandler

Returns a new instance of ProcessHandler.



203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
# File 'lib/procreate/win32/open4.rb', line 203

def initialize cmdline, params={}
  stdin, @stdout, @stderr = params.values_at :stdin, :stdout, :stderr
  if stdin
    raise NotImplementError, 'no stdin forwarding yet'
  end

  # create 3 pipes
  pw, pr, pe = Array.new(3) { create_pipe }

  set_handle_information pw.last,  HANDLE_FLAG_INHERIT, 0
  set_handle_information pr.first, HANDLE_FLAG_INHERIT, 0
  set_handle_information pe.first, HANDLE_FLAG_INHERIT, 0

  @phandle, hThread, @processId, dwThreadId = create_process cmdline.to_s, pw.first, pr.last, pe.last

  # we have to close the handles, so the pipes terminate with the process
  close_handle pw.first
  close_handle pr.last
  close_handle pe.last

  finalizer = self.class.finalizer [@phandle, pw.last, pr.first, pe.first]
  ObjectSpace.define_finalizer self, finalizer

  @pr, @pe = pr, pe
end

Class Method Details

.finalizer(handles) ⇒ Object



229
230
231
232
233
# File 'lib/procreate/win32/open4.rb', line 229

def self.finalizer handles
  proc do
    handles.each { |h| close_handle h }
  end
end

Instance Method Details

#relayObject



257
258
259
260
261
262
263
264
265
266
267
268
# File 'lib/procreate/win32/open4.rb', line 257

def relay
  stdout = ''
  avail = peek_named_pipe(@pr.first)
  stdout = read_file(@pr.first, avail) if avail > 0

  stderr = ''
  avail = peek_named_pipe(@pe.first)
  stderr = read_file(@pe.first, avail) if avail > 0

  @stdout << stdout if !stdout.empty? and @stdout
  @stderr << stderr if !stderr.empty? and @stderr
end

#runObject



235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
# File 'lib/procreate/win32/open4.rb', line 235

def run
  loop do
    relay

    case WaitForSingleObject @phandle, 10
    when WAIT_TIMEOUT
      # ok...
    when WAIT_OBJECT_0
      relay
      exit_code = [0].pack 'L'
      if GetExitCodeProcess @phandle, exit_code
        exit_status = exit_code.unpack('L')[0]
      end
      return exit_status
    when -1, WAIT_ABANDONED, WAIT_FAILED
      raise_last_error!
    end

    sleep 0.01
  end
end