Module: QDA::Filters::Win32Process

Defined in:
lib/weft/filters/win32backtick.rb

Overview

Used only on windows to enable calling other executables without the annoying command-prompt box that pops up when using Ruby backticks in a script running under rubyw.

Note - most of this code written by S Kroeger, see blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/155684

Defined Under Namespace

Classes: Win32popenIO

Constant Summary collapse

NORMAL_PRIORITY_CLASS =
0x00000020
STARTUP_INFO_SIZE =
68
PROCESS_INFO_SIZE =
16
SECURITY_ATTRIBUTES_SIZE =
12
ERROR_SUCCESS =
0x00
FORMAT_MESSAGE_FROM_SYSTEM =
0x1000
FORMAT_MESSAGE_ARGUMENT_ARRAY =
0x2000
HANDLE_FLAG_INHERIT =
1
HANDLE_FLAG_PROTECT_FROM_CLOSE =
2
STARTF_USESHOWWINDOW =
0x00000001
STARTF_USESTDHANDLES =
0x00000100

Class Method Summary collapse

Class Method Details

.backtick(command) ⇒ Object

The only useful public method in this class - receives a command line, and returns the output content and error content as a pair of strings. No shell expansion is carried out on the command line string.

output, errors = Win32Process::backtick('ls')


218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
# File 'lib/weft/filters/win32backtick.rb', line 218

def self.backtick(command)
  # create 3 pipes
  child_in_r, child_in_w = create_pipe
  child_out_r, child_out_w = create_pipe
  child_error_r, child_error_w = create_pipe

  # Ensure the write handle to the pipe for STDIN is not inherited.
  set_handle_information(child_in_w, HANDLE_FLAG_INHERIT, 0)
  set_handle_information(child_out_r, HANDLE_FLAG_INHERIT, 0)
  set_handle_information(child_error_r, HANDLE_FLAG_INHERIT, 0)

  processId, threadId = create_process( command, 
                                        child_in_r, 
                                        child_out_w, 
                                        child_error_w )
  # we have to close the handles, so the pipes terminate with the process
  close_handle(child_in_r)
  close_handle(child_out_w)
  close_handle(child_error_w)
  close_handle(child_in_w)
  io = Win32popenIO.new(child_out_r, child_in_w, child_error_r)
 
  out = io.read_all().gsub(/\r/, '')
  err = io.read_all_err().gsub(/\r/, '')
  return out, err
end

.close_handle(handle) ⇒ Object



85
86
87
88
# File 'lib/weft/filters/win32backtick.rb', line 85

def close_handle(handle)
  closeHandle = Win32API.new("kernel32", "CloseHandle", ['L'], 'I')
  raise_last_win_32_error if closeHandle.call(handle).zero?
end

.create_pipeObject

returns read and write handle



54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/weft/filters/win32backtick.rb', line 54

def create_pipe # returns read and write handle
  params = [
    'P', # pointer to read handle
    'P', # pointer to write handle
    'P', # pointer to security attributes
    'L'] # pipe size

  createPipe = Win32API.new("kernel32", "CreatePipe", params, 'I')

  read_handle, write_handle = [0].pack('I'), [0].pack('I')
  sec_attrs = [SECURITY_ATTRIBUTES_SIZE, 0, 1].pack('III')

  raise_last_win_32_error if createPipe.Call(read_handle,
                                              write_handle, sec_attrs, 0).zero?

  [read_handle.unpack('I')[0], write_handle.unpack('I')[0]]
end

.create_process(command, stdin, stdout, stderror) ⇒ Object



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/weft/filters/win32backtick.rb', line 90

def create_process(command, stdin, stdout, stderror)
  params = [
    'L', # IN LPCSTR lpApplicationName
    'P', # IN LPSTR lpCommandLine
    'L', # IN LPSECURITY_ATTRIBUTES lpProcessAttributes
    'L', # IN LPSECURITY_ATTRIBUTES lpThreadAttributes
    'L', # IN BOOL bInheritHandles
    'L', # IN DWORD dwCreationFlags
    'L', # IN LPVOID lpEnvironment
    'L', # IN LPCSTR lpCurrentDirectory
    'P', # IN LPSTARTUPINFOA lpStartupInfo
    'P']  # OUT LPPROCESS_INFORMATION lpProcessInformation

  startupInfo = [STARTUP_INFO_SIZE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW, 0,
    0, 0, stdin, stdout, stderror].pack('IIIIIIIIIIIISSIIII')

  processInfo = [0, 0, 0, 0].pack('IIII')
  command << 0

  createProcess = Win32API.new("kernel32", "CreateProcess", params, 'I')
  cp_args = [ 0, command, 0, 0, 1, 0, 0, 0, startupInfo, processInfo ]
  raise_last_win_32_error if createProcess.call(*cp_args).zero?

  hProcess, hThread, 
  dwProcessId, dwThreadId = processInfo.unpack('LLLL')

  close_handle(hProcess)
  close_handle(hThread)

  [dwProcessId, dwThreadId]
end

.peek_named_pipe(hFile) ⇒ Object



157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/weft/filters/win32backtick.rb', line 157

def peek_named_pipe(hFile)
  params = [
    'L', # handle to pipe to copy from
    'L', # pointer to data buffer
    'L', # size, in bytes, of data buffer
    'L', # pointer to number of bytes read
    'P', # pointer to total number of bytes available
    'L'] # pointer to unread bytes in this message

  available = [0].pack('I')
  peekNamedPipe = Win32API.new("kernel32", "PeekNamedPipe", params, 'I')

  return -1 if peekNamedPipe.Call(hFile, 0, 0, 0, available, 0).zero?

  available.unpack('I')[0]
end

.raise_last_win_32_errorObject



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/weft/filters/win32backtick.rb', line 28

def raise_last_win_32_error
  errorCode = Win32API.new("kernel32", "GetLastError", [], 'L').call
  if errorCode != ERROR_SUCCESS
    params = [
      'L', # IN DWORD dwFlags,
      'P', # IN LPCVOID lpSource,
      'L', # IN DWORD dwMessageId,
      'L', # IN DWORD dwLanguageId,
      'P', # OUT LPSTR lpBuffer,
      'L', # IN DWORD nSize,
      'P', # IN va_list *Arguments
    ]

    formatMessage = Win32API.new("kernel32", "FormatMessage", params, 'L')
    msg = ' ' * 255
    msgLength = formatMessage.call(FORMAT_MESSAGE_FROM_SYSTEM +
                                      FORMAT_MESSAGE_ARGUMENT_ARRAY, '', errorCode, 0, msg, 255, '')

    msg.gsub!(/\000/, '')
    msg.strip!
    raise msg
  else
    raise 'GetLastError returned ERROR_SUCCESS'
  end
end

.read_file(hFile) ⇒ Object



140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# File 'lib/weft/filters/win32backtick.rb', line 140

def read_file(hFile)
  params = [
    'L', # handle of file to read
    'P', # pointer to buffer that receives data
    'L', # number of bytes to read
    'P', # pointer to number of bytes read
    'L'] #pointer to structure for data

  number = [0].pack('I')
  buffer = ' ' * 255

  readFile = Win32API.new("kernel32", "ReadFile", params, 'I')
  return '' if readFile.call(hFile, buffer, 255, number, 0).zero?

  buffer[0...number.unpack('I')[0]]
end

.set_handle_information(handle, flags, value) ⇒ Object



72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/weft/filters/win32backtick.rb', line 72

def set_handle_information(handle, flags, value)
  params = [
    'L', # handle to an object
    'L', # specifies flags to change
    'L'] # specifies new values for flags

  setHandleInformation = Win32API.new("kernel32",
                                       "SetHandleInformation", params, 'I')
  raise_last_win_32_error if setHandleInformation.Call(handle,
                                                        flags, value).zero?
  nil
end

.write_file(hFile, buffer) ⇒ Object



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/weft/filters/win32backtick.rb', line 123

def write_file(hFile, buffer)
  params = [
    'L', # handle to file to write to
    'P', # pointer to data to write to file
    'L', # number of bytes to write
    'P', # pointer to number of bytes written
    'L'] # pointer to structure for overlapped I/O

  written = [0].pack('I')
  writeFile = Win32API.new("kernel32", "WriteFile", params, 'I')

  raise_last_win_32_error if writeFile.call(hFile, buffer, buffer.size,
                                             written, 0).zero?

  written.unpack('I')[0]
end