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’)



374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
# File 'lib/cosmos/win32/win32.rb', line 374

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



236
237
238
239
# File 'lib/cosmos/win32/win32.rb', line 236

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



203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
# File 'lib/cosmos/win32/win32.rb', line 203

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



241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
# File 'lib/cosmos/win32/win32.rb', line 241

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



311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
# File 'lib/cosmos/win32/win32.rb', line 311

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



177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
# File 'lib/cosmos/win32/win32.rb', line 177

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



293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
# File 'lib/cosmos/win32/win32.rb', line 293

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



222
223
224
225
226
227
228
229
230
231
232
233
234
# File 'lib/cosmos/win32/win32.rb', line 222

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



275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
# File 'lib/cosmos/win32/win32.rb', line 275

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