COM Scripting with JudoScript
By James Jianbo Huang December 2002 printer-friendly versionAbstract
Microsoft's COM and Java are the two most popular object models. JudoScript has native
support for both. In JudoScript, ActiveX controls are obtained via the operator
new com::
. Once obtained, the object's methods can be called and
properties accessed just like any other kinds of objects. One of the main reasons for
ActiveX scripting is to use Microsoft Office products such as Word and Excel; sample
code is presented to demonstrate their uses.
Currently ActiveX event handling is not supported due to the limitation of the
underlying JCom package, and out-bound parameters are not supported in a more meaningful
way. These situations may improve in the later versions.
Microsoft's Component Object Model (COM) and Java's object model are the two most
important object models to date. JudoScript, as a language designed for Java scripting, has
also native support for COM/ActiveX scripting. JudoScript seamlessly bridges these two
powerful object models (without the Microsoft JVM).
In the early days of Windows, most programs are monolithic executables. DLLs provide
limited flexibility and reusability. It was the "crazy" idea of embedding spreadsheets
in word documents and vice versa that prompted the concept of OLE, which eventually led
to the ubiquitous COM on Windows platforms. Today, most of the major Windows software
are presented in the form of ActiveX (what a funky name!), making them readily
scriptable with capable scripting languages like Visual Basic and JudoScript.
All COM objects are registered in the Windows registry when the software is
installed. Windows have APIs to obtain such objects via GUIDs (Globally Unique
IDentifiers). COM objects implements various interfaces; each interface is identified
by a GUID and is also registered on the system. Every single COM object implements an
interface called IUnknown, which is not useful in itself but allows clients to obtain
other interfaces that it implements. Hence, in order to use a COM object, you will
need to know its GUID and its interfaces. There are ways to describe and inspect those
interfaces offline, but this is not generally available at runtime and type matching
is challenging if possible at all. What this means is, general COM objects are not as
easily scriptable with scripting languages.
In order to make COM objects scriptable, Microsoft has defined a special interface called
IDispatch
, which has such an invoke()
method for any clients to
call named methods supported by the object. In particular, there are two pre-defined method
names, "GET" and "PUT", to access "properties" of the objects.
Therefore, ActiveX objects are scriptable COM objects that implement IDispatch
.
How do you know what methods to call using the invoke()
method? You as a
programmer need to consult the manual for that particular object. (You may ask, why bother
to have IDispatch interface anyhow, if you still need to know what the object supports?
Keep in mind that IDispatch permits tools like Visual Basic and JudoScript to pass on your
intention to call which method and get/set which property; without IDispatch, you may have
to write C/C++ code to access these objects, albeit it is possible.)
»»» Top «««
To get hold of an ActiveX control in JudoScript, use the new
with the
com::
namespace; once obtained, call its methods and access its
properties like any other types of objects in JudoScript.
Listing 1. vbscript.judo |
1: sc = new com::ScriptControl;
2: sc.Language = 'VBScript';
3:
4: for i=0; i<10; ++i {
5: println sc.Eval(i @ '+1001');
6: }
|
Microsoft Windows has aliases for ActiveX identifiers. In the above example, we
instantiates a "ScriptControl", set its Language
property to "VBScript",
and (on line 5) invokes its Eval()
method. To use GUID to find the
object, you must use the format in the following example:
Listing 2. ietest.judo |
1: ie = new com::'{0002DF01-0000-0000-C000-000000000046}';
2: //ie = new com::InternetExplorer.Application;
3: ie.Visible = true;
4: ie.AddressBar = true;
5: ie.StatusText = 'My Status Text';
6: println '----- Path: ', ie.Path;
7:
8: url = 'http://www.yahoo.com';
9: println '----- Visiting ', url;
10: ie.Navigate(url);
11: sleep(5000);
12:
13: url = 'http://www.google.com';
14: println '----- Visiting ', url;
15: ie.Navigate(url);
16: sleep(5000);
17:
18: println '----- close the browser.';
19: ie.Quit();
|
This program brings up the Internet Explorer, prints out the path for the executable
(line 6), visits Yahoo! and Google respectively for 5 seconds, then closes the
brower.
Like calling methods in Java, you can cast values to Java primitive types. For
ActiveX invocation, there is one more type, currency
.
One of the main reasons to script ActiveX controls is to script Microsoft Office
products such as Word and Excel. The following is an example that uses Excel.
Listing 3. exceltest.judo |
1: xl = new com::Excel.Application;
2: println 'Version: ', xl.Version;
3: xl.Visible = true;
4:
5: workbooks = xl.Workbooks;
6: workbook = workbooks.Add;
7: sheet = workbook.ActiveSheet;
8: a1 = sheet['Range', 'A1'];
9: a2 = sheet['Range', 'A2'];
10: a1.Value = 123.456;
11: a2.Formula = '=A1*2';
12: println 'a1 from excel: ', a1.Value;
13: println 'a2 from excel: ', a2.Value;
14:
15: workbook.Close();
16: xl.Quit();
|
It launches Excel, creates a new workbook, sets a value at cell "A1" and sets a
formula at cell "A2", then prints out the values at "A1" and "A2". On lines 8 and
9, it uses parameterized property get; its syntax is the same as the
multi-dimensional array access operator. Similarly, a simple property access for
ActiveX controls can be conceived as a single-dimensional array access. (It would
be preferrable to use a syntax like sheet.Range['A1']
; unfortunately
JudoScript would be confused by this, since the dot operator and [] operator in this
expression are two separate operations, whereas in this case it is really a single
operation.) The following is another example for Excel; it lists all the files in
the current directory and displays their attributes in Excel. It calculates the
sum of file sizes.
Listing 4. testexcel.judo |
1: excel = new com::Excel.Application;
2: excel.Visible = true;
3: println "Version: ", excel.Version;
4: println "UserName: ", excel.UserName;
5: println "Caption: ", excel.Caption;
6: println "Value: ", excel.Value;
7:
8: xlBooks = excel.Workbooks;
9: xlBook = xlBooks.Add;
10: xlSheets = xlBook.Worksheets;
11: xlSheet = xlSheets['Item', 1];
12: xlRange = xlSheet.Cells;
13:
14: xlRange['Item',1,1].Value = "Name";
15: xlRange['Item',1,2].Value = "Length";
16: xlRange['Item',1,3].Value = "Time";
17: xlRange['Item',1,4].Value = "Is-Dir";
18: xlRange['Item',1,5].Value = "Is-File";
19: xlRange['Item',1,6].Value = "Exists";
20: xlRange['Item',1,7].Value = "Writable";
21: xlRange['Item',1,8].Value = "Hidden";
22:
23: list './';
24: filenames = $$fs_result;
25: i = 2;
26: for file in filenames {
27: xlRange['Item',i,1].Value = file;
28: xlRange['Item',i,2].Value = file.fileLength();
29: xlRange['Item',i,3].Value = file.fileTime();
30: xlRange['Item',i,4].Value = file.isDirectory();
31: xlRange['Item',i,5].Value = file.isFile();
32: xlRange['Item',i,6].Value = file.fileExists();
33: xlRange['Item',i,7].Value = file.fileWritable();
34: xlRange['Item',i,8].Value = file.isFileHidden();
35: ++i;
36: }
37: ++i;
38: xlRange['Item',i,1].Value = "Total Size:";
39: xlRange['Item',i,2].Formula = '=Sum(B2:B' @ (filenames.length+1) @ ')';
40: xlRange.Columns.AutoFit();
41:
42: sleep(10000);
43: xlBook.Close(false,null,false);
44: excel.Quit();
|
The following is an example that uses Microsoft Word. It opens a Word document
and prints out its words and tables. Using this program as a template, you can
do a lot more with Word; you will need to know the Word's DOM, which is not the
topic of this article; this information is available in the help files of the
Word product, or any books about VBA for Word.
Listing 5. testword.judo |
1: wdApp = new com::Word.Application;
2: wdApp.Visible = true;
3:
4: wdDocuments = wdApp.Documents;
5: wdDocument = wdDocuments.Open(#args[0].toAbsolutePath());
6: println 'fullname=', wdDocument.FullName;
7:
8: wdWords = wdDocument.Words;
9: word_count = wdWords.Count;
10: for i from 1 to word_count {
11: println wdWords.Item(i).Text;
12: }
13:
14: wdTables = wdDocument.Tables;
15: println wdTables;
16: table_count = wdTables.Count;
17: println 'table count=', table_count;
18: for i from 0 to table_count-1 {
19: println 'tables[', i, "]=", wdTables.Item(i+1);
20: }
21:
22: sleep(3000);
23: wdApp.Quit();
|
One of the COM interface, IEnumVARIANT, represents a collection of values. It can
be returned by method calls. JudoScript directly converts an IEnumVARIANT into an array.
Listing 6. enumtest.judo |
1: excel = new com::Excel.Application;
2: excel.Visible = true;
3:
4: xlBooks = excel.Workbooks;
5: xlBook = xlBooks.Add;
6: xlSheets = xlBook.Worksheets;
7:
8: ary = xlSheets._NewEnum.getIEnumVARIANT();
9: for xlSheet in ary { println xlSheet.Name; }
10:
11: sleep(5000);
12: xlBook.Close(false,null,false);
13: excel.Quit();
|
»»» Top «««
At this writing, JudoScript uses the JCom
package for its ActiveX support. Java classes of JCom are already included in the
JudoScript. JCom's JNI DLL, jcom.dll, must be put in the PATH
environment
variable.
JCom package is a straightforward Java-COM bridge. Its API is concise and elegant,
and maps Java and COM data types well. However, JudoScript has a wish-list from a Java-COM
bridge provider that would support by-reference parameters and event handling. So JudoScript
has not ruled out the possibility of creating its own Java-COM implementation. For now,
if scripting Microsoft Word and Excel is functioning well, it is deemed good enough,
unless users strongly request the aforementioned features.
»»» Top «««
JudoScript bridges the two most powerful object models -- COM/ActiveX and Java. In JudoScript,
scripting ActiveX controls is no different than scripting any other objects, including
Java objects; the only difference is the way to obtain an object: ActiveX controls are
obtained via the new com::
operator; once obtained, its methods can be
invoked, and properties can be got and set. Parameterized property get is achieved with
the syntax of multi-dimensional array access. A predefined COM interface, IEnumVARIANT,
is interpreted by JudoScript as an array.
Currently, JudoScript does not handle ActiveX event handling, and by-reference parameters
are not explicitly supported, that is, the out-bound parameters are not supported.
This, however, may change in the future releases.
»»» Top «««
- vbscript.judo
- ietest.judo
- exceltest.judo
- testexcel.judo
- testword.judo
- enumtest.judo