[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

subprocess on Windows

Jeff Schwab

1/26/2006 2:23:00 PM

Whenever my Windows rubyw script launches a subprocess, a command window
pops up and immediately disappears. How can I keep this from happening?

Details:

I keep a daily log of what I do at work. At the command line, I type
"log" to edit the current day's log file. A Ruby script generates an
appropriate name for the file automatically, then launches vim.

Now I'm trying to use the Windows GUI, and I want to have an icon I can
click to launch gvim. Here's the code:

# log.rbw
# Launch gvim on today's logfile, e.g. on
# C:\Logs\2006\2006.01\2006.01.26.txt

logfile = Time.now.strftime('C:\Logs\%Y\%Y.%m\%Y.%m.%d.txt')
system("gvim", logfile)

When I click the icon, I get the expected behavior, except that a
Windows cmd.exe window flashes on the screen first. :(
5 Answers

Robert Klemme

1/26/2006 3:15:00 PM

0

Jeffrey Schwab wrote:
> Whenever my Windows rubyw script launches a subprocess, a command
> window pops up and immediately disappears. How can I keep this from
> happening?
>
> Details:
>
> I keep a daily log of what I do at work. At the command line, I type
> "log" to edit the current day's log file. A Ruby script generates an
> appropriate name for the file automatically, then launches vim.
>
> Now I'm trying to use the Windows GUI, and I want to have an icon I
> can click to launch gvim. Here's the code:
>
> # log.rbw
> # Launch gvim on today's logfile, e.g. on
> # C:\Logs\2006\2006.01\2006.01.26.txt
>
> logfile = Time.now.strftime('C:\Logs\%Y\%Y.%m\%Y.%m.%d.txt')
> system("gvim", logfile)
>
> When I click the icon, I get the expected behavior, except that a
> Windows cmd.exe window flashes on the screen first. :(

One thing is you can change the linke to open the window minimized. And I
think I remember seeing a rubyw or such in the windows distribution that
does not open a cmd shell. HTH

robert

Matthew Desmarais

1/26/2006 4:01:00 PM

0

Jeffrey Schwab wrote:
> Whenever my Windows rubyw script launches a subprocess, a command
> window pops up and immediately disappears. How can I keep this from
> happening?
>
> Details:
>
> I keep a daily log of what I do at work. At the command line, I type
> "log" to edit the current day's log file. A Ruby script generates an
> appropriate name for the file automatically, then launches vim.
>
> Now I'm trying to use the Windows GUI, and I want to have an icon I
> can click to launch gvim. Here's the code:
>
> # log.rbw
> # Launch gvim on today's logfile, e.g. on
> # C:\Logs\2006\2006.01\2006.01.26.txt
>
> logfile = Time.now.strftime('C:\Logs\%Y\%Y.%m\%Y.%m.%d.txt')
> system("gvim", logfile)
>
> When I click the icon, I get the expected behavior, except that a
> Windows cmd.exe window flashes on the screen first. :(
>
>
You may want Kernel.exec instead of Kernel.system. See ri Kernel.exec
and ri Kernel.system for more info.


Alex Fenton

1/26/2006 7:15:00 PM

0

There was a thread on c.l.r on this about six-nine months ago. Someone found a way to avoid getting a window by opening the process through the Win32 API.

I can't find the original thread right now, but I've got an implementation in my app, Weft QDA:
http://rubyforge.org/cgi-bin/viewcvs.cgi/weft-qda/lib/weft/filters/win32backtick.rb?root=weft-qda&rev=1.1&v...

(attached below, because rubyforge viewCVS seems slow at the moment)

This code is 95% cut and pasted from that thread, so I can't claim any credit for it nor explain what it does ... I just added an interface to it so that it acted like backticks. It works very well though and I am grateful for whoever navigated some dreadful looking API calls.

alex


Jeffrey Schwab wrote:
> Whenever my Windows rubyw script launches a subprocess, a command window
> pops up and immediately disappears. How can I keep this from happening?
>
> Details:
>
> I keep a daily log of what I do at work. At the command line, I type
> "log" to edit the current day's log file. A Ruby script generates an
> appropriate name for the file automatically, then launches vim.
>
> Now I'm trying to use the Windows GUI, and I want to have an icon I can
> click to launch gvim. Here's the code:
>
> # log.rbw
> # Launch gvim on today's logfile, e.g. on
> # C:\Logs\2006\2006.01\2006.01.26.txt
>
> logfile = Time.now.strftime('C:\Logs\%Y\%Y.%m\%Y.%m.%d.txt')
> system("gvim", logfile)
>
> When I click the icon, I get the expected behavior, except that a
> Windows cmd.exe window flashes on the screen first. :(

require 'Win32API'

module QDA
module Filters
# 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
# http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-t...
module Win32Process
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 << self
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

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

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

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

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

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

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

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
end

class Win32popenIO
def initialize (hRead, hWrite, hError)
@hRead = hRead
@hWrite = hWrite
@hError = hError
end

def write data
Win32Process::write_file(@hWrite, data.to_s)
end

def read
sleep(0.01) while Win32Process::peek_named_pipe(@hRead).zero?
Win32Process::read_file(@hRead)
end

def read_all
all = ''
until (buffer = read).empty?
all << buffer
end
all
end

def read_err
sleep(0.01) while Win32Process::peek_named_pipe(@hError).zero?
Win32Process::read_file(@hError)
end

def read_all_err
all = ''
until (buffer = read_err).empty?
all << buffer
end
all
end
end

# 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')
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
end
end
end

Wilson Bilkovich

1/26/2006 9:06:00 PM

0

On 1/26/06, Jeffrey Schwab <jeff@schwabcenter.com> wrote:
> Whenever my Windows rubyw script launches a subprocess, a command window
> pops up and immediately disappears. How can I keep this from happening?
>
> Details:
>
> I keep a daily log of what I do at work. At the command line, I type
> "log" to edit the current day's log file. A Ruby script generates an
> appropriate name for the file automatically, then launches vim.
>
> Now I'm trying to use the Windows GUI, and I want to have an icon I can
> click to launch gvim. Here's the code:
>
> # log.rbw
> # Launch gvim on today's logfile, e.g. on
> # C:\Logs\2006\2006.01\2006.01.26.txt
>
> logfile = Time.now.strftime('C:\Logs\%Y\%Y.%m\%Y.%m.%d.txt')
> system("gvim", logfile)
>
> When I click the icon, I get the expected behavior, except that a
> Windows cmd.exe window flashes on the screen first. :(

Try:
`start /B gvim #{logfile}`


Jeff Schwab

1/27/2006 3:11:00 AM

0

Alex Fenton wrote:
> There was a thread on c.l.r on this about six-nine months ago. Someone
> found a way to avoid getting a window by opening the process through
> the Win32 API.
>
> I can't find the original thread right now, but I've got an
> implementation in my app, Weft QDA:
> http://rubyforge.org/cgi-bin/viewcvs.cgi/weft-qda/lib/weft/filters/win32backtick.rb?root=weft-qda&rev=1.1&v...


Thank you! That did it.

Thanks to everybody who responded. This is a great community. :)

I did try exec instead of system, but to no avail. Setting the window
to run minimized doesn't help: rubyw already runs without a window.
Similarly, using 'start' does not help. The problem is that *any*
backticks, or call to system() or exec(), launches a command window.
Ruby's doing what it's supposed to do, and so is Vim. The problem is
the convoluted Windows API, for which I now have a nice encapsulation.