Section 14.6. The Windows One-Click Installer


14.5. Scripting in Microsoft Windows

Like the ski resort full of girls hunting for husbands and husbands hunting for girls, the situation is not as symmetrical as it might seem.

Alan Lindsay Mackay

It has been said that Ruby has a UNIX bias. In a sense, this is true; it was conceived in a UNIX environment and works best there. Yet there are other ports out there at the time of this writing, including Macintosh, and there are other ports in progress, such as Palm OS. But if UNIX is the primary platform for Ruby, the secondary platform is Windows.

Windows users certainly aren't left out in the cold. Windows-based tools and libraries are in existence, and more are being created. Much of Ruby is platform-independent anyhow, even the threading capabilities; most of the platform difficulties occur in the areas of I/O, process management, and other similar low-level operations.

In the past, one problem for Windows users was that there were different variants of Ruby for the Windows platforms. The interpreter might have been built with gcc or Visual C, it might or might not depend on the Cygwin DLL, and so on. In recent years, we have had the luxury of using the "one-click" installer for Windows (see section 14.6, "The Windows One-Click Installer.")

The environment is changing too rapidly to document at this point, but this section discusses a few of the high points in Windows scripting and automation. These techniques and utilities should work for anyone, and if there are problems, the online support community is helpful with such things.

14.5.1. Using Win32API

The Win32API is exceptionally powerful if you want to code at a fairly low level. Essentially it allows access to any Windows API function in any DLL, making it callable from Ruby code.

The specified function is instantiated as an object, with relevant parameters precisely describing the function being passed into the new method. The first parameter, a string, identifies the DLL containing the function (such as crtdll). The second parameter is the name of the function itself. The third parameter is an array of strings identifying the types of the function parameters (the import array), and the fourth parameter is a string specifying the function's return type.

The import array can contain these (not case-sensitive) values:

I    Integer L    Number N    Number P    Pointer to a string


The export string can also contain any one of these. Additionally it can take the value "V", meaning "void."

After we have created this object, we can invoke its call method to call the Windows function. Note that Call is an alias.

Here we call the Windows function GetCursorPos, which returns a pointer to a POINT structure. This structure consists of two long fields; we can use unpack to examine these fields and retrieve their values.

require 'Win32API' result = "0"*8   # Eight bytes (enough for two longs) getCursorXY = Win32API.new("user32","GetCursorPos",["P"],"V") getCursorXY.call(result) x, y = result.unpack("LL")  # Two longs


Sometimes we need to pass in complex binary data, whereas in this case it was passed back to us. In that case, we could obviously use pack to pack the data into a string.

Obviously this technique has many possible applications. Two other code fragments can be seen in section 10.1.20, "Grabbing Characters from the Keyboard," and 14.1.1, "Using system and exec."

14.5.2. Using Win32OLE

The Win32OLE extension library (actually spelled in lowercase, win32ole) provides an interface to Windows OLE automation. Your Ruby code can act as a client for any OLE automation server such as Microsoft Word, Outlook, and Internet Explorer, and many third-party software products.

To interact with an external application, we first create a new object of the WIN32OLE class. This object is used to access all the exposed properties and methods of the specified application.

In this example, we associate an object with the Microsoft Word application. We set the visible attribute to true, and eventually we quit, exiting the application.

require "win32ole" word = WIN32OLE.new "Word.Application" word.visible = true # ... word.quit


Every property of the automation server is reflected as an attribute of the object. These can be set or examined at will.

An alternate notation uses a hashlike construct to access these properties.

player["FileName"] = "file.wav" name = player["FileName"] # Equivalent to these statements: # player.FileName = "file.wav" # name = player.FileName


One advantage of this is that it can easily handle the more "programmatic" situations as shown in this contrived example:

puts "Enter the property name" prop = gets puts "Enter the new value" val = gets old = obj[prop] obj[prop] = val puts "#{prop} was #{old}... now is #{obj[prop]}"


But let's look at some more concrete examples. Here is a code fragment that takes a filename from the command line, passes it into Microsoft Word, and prints the file:

require "win32ole" print "Enter the filename to print: " docfile = gets word = WIN32OLE.new "Word.Application" word.visible = true word.documents.open docfile word.options.printBackground = false # We could also set printBackground to true, but we # would have to sleep until the file all got sent to # the printer buffer before we quit... word.activeDocument.printOut word.quit


The following is an example of playing a WAV file. It has the disadvantage of an arbitrary sleep at the end rather than waiting for the output to finish. Fixing this is left as an exercise.

require "win32ole" sound = WIN32OLE.new("MCI.MMcontrol") wav = "c:\\windows\\media\\tada.wav" sound.fileName = wav sound.autoEnable = true sound.command = "Open" sound.command = "Play" sleep 7


Listing 14.2 uses Internet Explorer to generate a text input box.

Listing 14.2. Browser Text Input Box

require "win32ole" def ieInputBox( msg, default )   ie = WIN32OLE.new("InternetExplorer.Application");   ie.visible  = false   ie.navigate "about:blank"   sleep 0.01 while (ie.busy)   script = ie.Document.Script;   result = script.prompt(msg,default);   ie.quit   result end # Main... result = ieInputBox( "Please enter your name",                      "Dave Bowman") if result   puts result else   puts "User pressed Cancel" end

In Listing 14.3, we open a small IE window and write HTML to it.

Listing 14.3. Writing to a Browser Window Requires "win32ole"

html = <<EOF <html> <body> <h3>And now for something</h3> <h2>completely</h2> <h1>different...</h1> </body> </html> EOF ie = WIN32OLE.new("InternetExplorer.Application"); ie.left       = 150 ie.top        = 150 ie.height     = 200 ie.width      = 300 ie.menubar    = 0 ie.toolbar    = 0 ie.navigate "about:blank" ie.visible=TRUE; ie.document.open ie.document.write html ie.document.close sleep 5 ie.quit

In the following example we open a file dialog box and allow the user to select a file from a list:

require "win32ole" cd = WIN32OLE.new("MSComDlg.CommonDialog") # Set file filter cd.filter = "All Files(*.*)|*.*" +             "|Ruby Files(*.rb)|*.rb" cd.filterIndex = 2 cd.maxFileSize = 128    # Set MaxFileSize cd.showOpen() file = cd.fileName      # Retrieve file, path if not file or file==""    puts "No filename entered." else    puts "The user selected: #{file}\n" end


And finally, the following fragment will discover the IP address of the local machine:

require "win32ole" ws = WIN32OLE.new "MSWinsock.Winsock" # Retrieve LocalIP property ipAddress = ws.localIP puts "The local IP is : #{ipAddress}"


As you can see, the possibilities are limitless. Have fun, and don't forget to share your code with others.

14.5.3. Using ActiveScriptRuby

You have probably used Internet Explorer at some point to view a web page that contained embedded JavaScript or VBScript code. (We'll ignore the differences between JScript and JavaScript here.)

You can do the same thing with ActiveScriptRuby, which is like a bridge between COM and Ruby. For example, we can embed Ruby in an HTML page (as seen in Listing 14.4).

Listing 14.4. Ruby Embedded in HTML

<html> <script language="RubyScript">   # This is Ruby code...   def helloMethod     @window.alert "Running Ruby Inside!"   end </script> <body> Here is an input button... <input id=Hello type=button onclick="helloMethod"        language="RubyScript"> </body> </html>

Using this technique of embedding Ruby, we can call Ruby code from any native Windows application that supports the IActiveScript interface, such as Internet Explorer or WScript (the WSH executable). You can visit arton's site (http://www.geocities.co.jp/SiliconValley-PaolAlto/9251/rubymain.html) for more information.




The Ruby Way(c) Solutions and Techniques in Ruby Programming
The Ruby Way, Second Edition: Solutions and Techniques in Ruby Programming (2nd Edition)
ISBN: 0672328844
EAN: 2147483647
Year: 2004
Pages: 269
Authors: Hal Fulton

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net