Scriptom is an optional Groovy module developed by Guillaume Laforge leveraging the Jacob library (JAva COm Bridge). Once installed in your Groovy installation, you can use a wrapper to script any ActiveX or COM component from within your Groovy scripts. Of course, this module can be used on Windows only.
Scriptom is especially interesting if you are developing Groovy shell scripts under Windows. You can combine both Groovy code and any Java library with the platform-specific features available to Windows Scripting Host or OLE COM automation from Office.
The easiest way for installing Scriptom is to unzip the Zip bundle in your GROOVY_HOME directory.
The distribution contains the jacob.jar and jacob.dll, and the scriptom.jar. The DLL needs to be in the bin directory, or in your java.library.path to be loaded by jacob.jar.
If you are brave enough and prefer using the very latest fresh version from CVS Head, you can build Scriptom from sources. Checkout modules/scriptom, and use Maven to do the installation automatically. If your GROOVY_HOME points at the target/install directory of your groovy-core source tree, just type:
maven
Otherwise, if you have installed Groovy in a different directory, you have two possibilities, either you change the property groovy.install.staging.dest to your GROOVY_HOME directory in the project.properties file, and run maven, or you can type:
maven -Dgroovy.install.staging.dest=%GROOVY_HOME%
Let's say we want to script Internet Explorer. First, we're going to import the ActiveX proxy class.
Then, we're going to create a GroovyObjectSupport wrapper around the ActiveXComponent class of Jacob. And now, we're ready to use properties or methods from the component:
import org.codehaus.groovy.scriptom.ActiveXProxy // instanciate Internet Explorer explorer = new ActiveXProxy("InternetExplorer.Application") // set its properties explorer.Visible = true explorer.AddressBar = true // navigate to a site by calling the Navigate() method explorer.Navigate("http://glaforge.free.fr/weblog")
Note however that explorer.Visible returns a proxy, if you want to get the real value of that property, you will have to use the expression explorer.Visible.value or explorer.Visible.getValue().
For the moment, Scriptom is in a beta stage, so you may encounter some bugs or limitations with certain ActiveX or COM component, so don't hesitate to post bugs either in JIRA or on the mailing lists. There may be some issues with the mappings of certain objects returned by the component and the Java/Groovy counterpart.
An important limitation for the first release is that it is not yet possible to subscribe to events generated by the components you are scripting. In the next releases, I hope I will be able to let you define your own event handlers with closures, with something like:
import org.codehaus.groovy.scriptom.ActiveXProxy explorer = new ActiveXProxy("InternetExplorer.Application") explorer.events.OnQuit = { println "Quit" }
But for the moment, event callbacks are not supported.
If you checkout the Scriptom sources, you will find a few samples in the src/script directory.
I will show you some samples in the following sub-sections.
import org.codehaus.groovy.scriptom.ActiveXProxy // instanciate Internet Explorer explorer = new ActiveXProxy("InternetExplorer.Application") // set its properties explorer.Visible = true explorer.AddressBar = true // navigate to a site explorer.Navigate("http://glaforge.free.fr/weblog") Thread.sleep(1000) explorer.StatusText = "Guillaume Laforge's weblog" Thread.sleep(2000) // quit Internet Explorer explorer.Quit()
import org.codehaus.groovy.scriptom.ActiveXProxy // create a proxy for Excel xls = new ActiveXProxy("Excel.Application") xls.Visible = true Thread.sleep(1000) // get the workbooks object workbooks = xls.Workbooks // add a new workbook workbook = workbooks.Add() // select the active sheet sheet = workbook.ActiveSheet // get a handle on two cells a1 = sheet.Range('A1') a2 = sheet.Range('A2') // sets a value for A1 a1.Value = 123.456 // defines a formula in A2 a2.Formula = '=A1*2' println "a1: ${a1.Value.value}" println "a2: ${a2.Value.getValue()}" // close the workbook without asking for saving the file workbook.Close(false, null, false) // quits excel xls.Quit()
Warning: on my machine (WinXP Home), there is still an Excel.exe process running. I have no clue why Excel is still running.
import org.codehaus.groovy.scriptom.ActiveXProxy // invoke some VBScript from Groovy and get the results! sc = new ActiveXProxy("ScriptControl") sc.Language = "VBScript" println sc.Eval("1 + 1").value
import org.codehaus.groovy.scriptom.ActiveXProxy // showing the current directory cmd = new ActiveXProxy("Scripting.FileSystemObject") println cmd.GetAbsolutePathName(".").value sh = new ActiveXProxy("Shell.Application") // minimizing all opened windows sh.MinimizeAll() // opens an Explorer at the current location sh.Explore(cmd.GetAbsolutePathName(".").value) // choosing a folder from a native windows directory chooser folder = sh.BrowseForFolder(0, "Choose a folder", 0) println folder.Items().Item().Path.value wshell = new ActiveXProxy("WScript.Shell") // create a popup wshell.popup("Groovy popup") // show some key from the registry key = "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\User Agent" println wshell.RegRead(key).value net = new ActiveXProxy("WScript.Network") // prints the computer name println net.ComputerName.value
import org.codehaus.groovy.scriptom.ActiveXProxy import java.io.File // create a proxy for the Shell object sh = new ActiveXProxy("Shell.Application") // use a Windows standard folder chooser folder = sh.BrowseForFolder(0, "Choose a folder with wav files", 0) // get the folder chosen folderName = folder.Items().Item().Path.value println "Playing Wav files from: ${folderName}" // create a Windows Media Player (from its Class ID) player = new ActiveXProxy("clsid:{6BF52A52-394A-11D3-B153-00C04F79FAA6}") // for each file in the folder new File(folderName).eachFile{ |file| if (file.name.endsWith("wav")) { println file player.URL = file.absolutePath // play the wav for one second control = player.controls.play() Thread.sleep(1000) } } // close the player player.close()
When event callbacks are supported, you will be able to subscribe to the player.statusChange event, so that you can play the wav entirely, before loading a new sample (instead of listening only to the first second of each sample).