Talking to other applications on windows


#1

Dear list

For a customer I’m building a GUI for a analytical application. The
analytical algorithm is confidential and implemented in a seperate .exe
program. However I am very pleased to have the customer agreeing to
build
all of the surrounding application in Ruby.

The problem comes when calling the .exe program from my GUI-application.
As of today I simply use the bactick operator to call the .exe with
appropriate input:

 result = `calculate.exe 1,2,3,4,5,6 `

The result from calculate.exe is written to screen and stored as result.
On a typical run, a couple hundred of these are executed, and everything
works fine except one simple detail:

Every calculation opens and closes a unique DOS-window.

No need to elaborate, you all understand how 200 blinking DOS-windows
does
not look good. The number to aim for here is 0. How can this be done?

Ideas:
I) Spawn each execution in the background.
II) Don’t communicate over write to screen / read from screen
- SOAP?
- XML-RPC?
- CORBA?
III) Windows service?

Some degrees of freedom exist as the customer is quite skilled and
willing
to make some modifications. calculate.exe is written in c#.

If anyone has any ideas, I would be very glad for all suggestions.

All the best
Jon Egil S.


#2

Pau

Thank you for your reply.

It’s easier than that, just make use of cmdow:
http://www.commandline.co.uk/cmdow/

Looks nice, but swallows output from the .EXE.

But I think it would be better if you had a DLL instead of a .EXE and you
could call the functions in the DLL instead of running the .EXE.

OK, I can try, but how do I call DLLs from within Ruby? Through
WIN32OLE?

All the best


#3

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On Saturday 21 January 2006 21:26, Jon Egil S. wrote:

It’s easier than that, just make use of cmdow:
http://www.commandline.co.uk/cmdow/

But I think it would be better if you had a DLL instead of a .EXE and
you
could call the functions in the DLL instead of running the .EXE.

I) Spawn each execution in the background.

All the best
Jon Egil S.


Pau Garcia i Quiles
http://www.elpauer.org
(En general no puedo contestar antes de 10 días)
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.1 (GNU/Linux)

iD8DBQFD0pyE/DzYv9iGJzsRArU0AJ4kk9FFlYEmfIt7x7nevUD/+PIhZgCgkWS+
bFrLrrzqhuWY3CSVACyfszU=
=4ZEo
-----END PGP SIGNATURE-----


#4

Jon Egil S. wrote:

Some degrees of freedom exist as the customer is quite skilled and willing
to make some modifications. calculate.exe is written in c#.

If anyone has any ideas, I would be very glad for all suggestions.

Add an XML-RPC (or SOAP, though probably overkill) API to the C# app and
have it run as a service?

James B.

http://www.ruby-doc.org - Ruby Help & Documentation
http://www.artima.com/rubycs/ - The Journal By & For Rubyists
http://www.rubystuff.com - The Ruby Store for Ruby Stuff
http://www.jamesbritt.com - Playing with Better Toys
http://www.30secondrule.com - Building Better Tools


#5

Jon Egil S. wrote:

[…]
No need to elaborate, you all understand how 200 blinking DOS-windows does
not look good. The number to aim for here is 0. How can this be done?

The windows are your complete problem?

look at this, you can uses it like

io = popen(‘ver’)
puts io.read_all

or

io = popen(‘cmd’)
io.write(“ping localhost\nexit\n”)

while !(buffer = io.read).empty?
print buffer.gsub("\r", ‘’)
end

and there is no window poping up. (does work for windows only, but that
seems to be ok for you, right?)

Here is the complete code, i posted this 11.09.2005 on this list.

cheers

Simon

require ‘Win32API’

$stdout.sync = true

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

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’)
raise_last_win_32_error if createProcess.call(0,
command, 0, 0, 1, 0, 0, 0, startupInfo, processInfo).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

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

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

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

def read_all
all = ‘’
while !(buffer = read).empty?
all << buffer
end
all
end
end

def popen(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(ENV[‘ComSpec’] + ’ /C ’ +
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)

Win32popenIO.new(child_out_r, child_in_w)
end

if $0 == FILE
io = popen(‘ver’)
puts io.read_all

io = popen(‘cmd’)
io.write(“ping localhost\nexit\n”)

while !(buffer = io.read).empty?
print buffer.gsub("\r", ‘’)
end
end