Module: QDA::Filters::Win32Process

Defined in:
lib/openc3/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’)



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

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



239
240
241
242
# File 'lib/openc3/win32/win32.rb', line 239

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

.create_pipeObject

returns read and write handle



206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
# File 'lib/openc3/win32/win32.rb', line 206

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



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
274
275
276
# File 'lib/openc3/win32/win32.rb', line 244

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



314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
# File 'lib/openc3/win32/win32.rb', line 314

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



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

def raise_last_win_32_error
  errorCode = OpenC3::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 = OpenC3::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



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

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



225
226
227
228
229
230
231
232
233
234
235
236
237
# File 'lib/openc3/win32/win32.rb', line 225

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



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

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