Module: QDA::Filters::Win32Process

Defined in:
lib/cosmos/win32/win32.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.

Usage: output, errors = QDA::Filters::Win32Process::backtick(‘dir’)

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.

Usage: output, errors = QDA::Filters::Win32Process::backtick(‘dir’)



359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
# File 'lib/cosmos/win32/win32.rb', line 359

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)

  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



225
226
227
228
# File 'lib/cosmos/win32/win32.rb', line 225

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

.create_pipeObject

returns read and write handle



194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
# File 'lib/cosmos/win32/win32.rb', line 194

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 = Cosmos::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



230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
# File 'lib/cosmos/win32/win32.rb', line 230

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 = Cosmos::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



297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
# File 'lib/cosmos/win32/win32.rb', line 297

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 = Cosmos::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



168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
# File 'lib/cosmos/win32/win32.rb', line 168

def raise_last_win_32_error
  errorCode = Cosmos::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 = Cosmos::Win32API.new("kernel32", "FormatMessage", params, 'L')
    msg = ' ' * 255
    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



280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
# File 'lib/cosmos/win32/win32.rb', line 280

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 = Cosmos::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



212
213
214
215
216
217
218
219
220
221
222
223
# File 'lib/cosmos/win32/win32.rb', line 212

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 = Cosmos::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



263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
# File 'lib/cosmos/win32/win32.rb', line 263

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 = Cosmos::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