Automating OpenOffice.org
Introduction
The OpenOffice.org (OOo) supports Microsoft's Automation technology on different Windows platforms ( Windows 95,98, ME, 2000, NT4). It enables clients to control the office externally. Client programs can be contained within executables or scripts. In order to make use of the Automation capability, a client must be coded in a programming language that supports Automation. There are a variety of appropriate languages and development environments available, such as Visual C++, Visual Basic, Delphi, VBScript and JScript. In order to use a scripting language one needs a script controller that executes the script. Common controllers are the Internet Explorer as well as the Windows Script Host (WSH).
To give you an impression on how Automation works with OOo, here is a quick example:
'The service manager is always the starting point
'If there is no office running then an office is started up
Set objServiceManager= WScript.CreateObject("com.sun.star.ServiceManager")
'Create the Desktop
Set objDesktop= objServiceManager.createInstance("com.sun.star.frame.Desktop")
'Open a new empty writer document
Dim args()
Set objDocument= objDesktop.loadComponentFromURL("private:factory/swriter",_
"_blank", 0, args)
'Create a text object
Set objText= objDocument.getText
'Create a cursor object
Set objCursor= objText.createTextCursor
'Inserting some Text
objText.insertString objCursor, "The first line in the newly created text document."&_
vbLf, false
This script opens a new writer document and inserts some text. If OOo is not already
running, then an instance is started up automatically.
To run this example put the code into a file named test.vbs and run it with the
Windows Script Host (WSH). That can be done by entering the command line:
cscript test.vbs
in a command line window. Alternatively one can double click the file entry in the Explorer (if in doubt, look at the documentation at http://msdn.microsoft.com/scripting/default.htm). As you may have noticed, this examples is written in VBScript but you can also use JScript with the WSH.
Automation Objects
In order to do automating tasks by an external client one needs to know what automation objects are offered by the server. Currently the The StarOffice Programmers Tutorial provides this information.
Service Manager
As shown in the example at the beginning of this document, one creates a service manager first. The service manager is the starting point for all external automation tasks. It can be instantiated as every ordinary ActiveX control. How this is done, depends on the programming language being used. The WSH, for example, provides a function on the WScript object that performs instantiation.
Set objServiceManager= WScript.CreateObject("com.sun.star.ServiceManager")
This function can also be used in JScript when it is run by the WSH.
Alternatively one can use the ActiveXObject
object.
var objServiceManager= new ActiveXObject("com.sun.star.ServiceManager");
Once instantiated, the service manager allows access to different office components, for example
Set objDesktop= objServiceManager.createInstance("com.sun.star.frame.Desktop")
This is essentially the same as calling
objDesktop= createunoservice("com.sun.star.frame.Desktop")
as is done within StarBasic (see StarOffice Programmers Tutorial).
Creation of Types in untyped Languages
Languages, such as VBScript and JScript, do not provide types. That is, the declaration of variables do not require a type specifier:
//JScript
var variableOfFloat;
var variableOfBool;
'VBScript
Dim variableOfFloat
Dim variableOfBool
// C++
float variableOfFloat;
bool variableOfBool;
In some rare situations you might need to have a concrete type. One can create them in two ways:
Set objServiceManager= WScript.CreateObject("com.sun.star.ServiceManager")
'Create the CoreReflection service that is later used to create structs
Set objCoreReflection= objServiceManager.createInstance(_
"com.sun.star.reflection.CoreReflection")
'get a type description class for float
Set classSize= objCoreReflection.forName("float")
'create the actual object
Dim aFloat
classSize.createObject aFloat
The other way goes like this:
Set objServiceManager= WScript.CreateObject("com.sun.star.ServiceManager")
Set aFloat= objServiceManager.Bridge_GetValueObject()
aFloat.Set "float", 3.14
The second approach uses a special object, which was dubbed
"ValueObject"
, that can be obtained from any office object by calling
Bridge_GetValueObject
. This value must be explicitly told what type it
represents, for more information about this object see the documentation
about the OLE bridge.
You should try to use concrete types when a function that takes an any
argument produces an error. Such an error could indicate a type mismatch.
Background Information
The interfaces of the office usually take parameters of concrete types.
However, there is a type, the "any"
, that can contain values of
different types, much as the VARIANT
type. When a function expects an any
then
the OLE bridge does not know what exactly has to be in the any
but it tries to
convert the scripting parameter according to fixed rules (e.g. long
to long
,
double
to double
). Let us have a look at these two functions:
void funcA( float f);
void funcB( any a);
When funcA is called in a script, then the OLE bridge receives a VARIANT
argument. The bridge knows that funcA
expects a float
, therefore it converts
the VARIANT
into a float
value. When funcB
is called, the bridge needs to
convert the value within the any
. However, the available type information about
the function only tells the argument type, which is an any
in this case. Without
type information for the contained type, the bridge converts the value into an
UNO type according to build-in conversion rules.
An example:
someObj.funcB 3.14
The bridge will receive a VARIANT
that contains a double
(VBScript,
JScript). The bridge will do a default conversion into a double
and create
an any
which is being assigned the double
. The implementation of
funcB
receives an
argument of type any
that contains a value of type double
. A tolerant
implementation should convert an any
into the expected type. But
unfortunately, this
is not always case. If funcB
is not tolerant and expects a float
rather then a
double
, then it might throw an exception.
In most cases the bridge will be able to do a correct conversion. However, it will not work as shown in example above.
A possible source of errors is the XPropertySet
interface with its methods
setPropertyValue
and getPropertyValue
which are frequently used when
writing
automating programs.
Creation of Structs
If the OOo API requires a struct as argument then the struct has to be
obtained from the office. It is not possible to declare a struct oneself. To
make this more intelligible, let us assume there is an office function that
takes a struct of type Size
.
// the interface function, that will be called from script
void XShape::setSize( Size aSize)
and the struct is declared as follows:
// idl
struct Size
{
long Width;
long Height;
}
You cannot write code like this ( VBScript):
Class Size
Dim Width
Dim Height
End Class
'obtain object that implements XShape
...
'now set the size
call objXShape.setSize( new Size)
There are to ways to create the struct
Set objServiceManager= WScript.CreateObject("com.sun.star.ServiceManager")
'Create the CoreReflection service that is later used to create structs
Set objCoreReflection= objServiceManager.createInstance(_
"com.sun.star.reflection.CoreReflection")
'get a type description class for Size
Set classSize= objCoreReflection.forName( "Size" )
'create the actual object
Dim aSize
classSize.createObject aStruct
'use aSize
aSize.Width= 100
aSize.Height= 12
objXShape.setSize aSize
And this is the other way
Set objServiceManager= WScript.CreateObject("com.sun.star.ServiceManager")
Set aSize= objServiceManager.Bridge_GetStruct("Size")
'use aSize
aSize.Width= 100
aSize.Height= 12
objXShape.setSize aSize
The Bridge_GetStruct
function can be called on any OOo object. The function
is also available in JScript.
Out Parameter
Lots of interface functions take out or in/out - parameter. In some languages, such as Jscript and Java, those function arguments are not supported. To use those functions regardless, we specified that in/out and out parameters are passed as arrays. The example below shows how this is done in JScript.
// the function takes an out-parameter
var out= new Array();
object.functionOut(out);
var value= out[0];
// the function takes an in/out-parameter
var inout= new Array();
inout[0]=123;
object.functionInOut( inout);
var value= inout[0];
As one can tell from the examples, the value of the out-parameter is accessible at index 0 within the array. For in/out - parameters, one puts the in - value at index 0 of the array.
One can also use ValueObjects
as in/out and out parameter. See the documentation
about the OLE bridge for more details.
Listener Objects (Event Sinks)
Some components send events. In order to receive events, one has to implement a listener. A listener is distinguished by implementing a listener interface. To receive those events, one has to connect the event source with the listener. An event source usually offers a function, that takes the listener interface as argument. One only has to implement the listener interface and pass an instance of the listener as argument to the appropriate function.
This does not only work with listeners. It is possible to implement all other interfaces an pass them to the office component. Currently, those interfaces can only be implemented in C++ and in JScript.
You can find more details in the documentation about the OLE bridge.
Visual Basic specific hints
The UNO interface functions which do not have a return value (i.e.
return void
), are mapped to sub routines, for example:
// definition of UNO function
void func( long val);
'VBScript call
func val
'or
call func( val)