Jawin Userguide - Calling a Scriptable COM Interface

Introduction

This part of the userguide, covers how to use Jawin when calling scriptable COM interfaces, ie. COM interfaces implementing the IDispatch interface. The information herein covers the "low-level" code, close to Jawin. This is the form of code the Jawin Type Browser generates - this generated code should be used instead of manually writing this "low-level" code. If the Jawin Type Browser is not able to generate complete stub-code for a COM interface, the guidelines in this document should be followed for manually writing the stub-code.

Content

  1. Quick 'n Dirty Overview of COM terms
  2. Using org.jawin.DispatchPtr to call a scriptable COM component
    1. Get properties
    2. Set properties
    3. Invoking methods
  3. Error Handling
  4. Threading Issues
  5. Additional Resources

1. Quick 'n Dirty Overview of COM terms

The COM dictionary is full of cryptic terms and abbrevations that one must know a minimum of to use Jawin succesfully. If terms such as GUID, CLSID, IID or ProgID are only vague known to you, please take the time to read the quick overview of these terms in section 1 in the Userguide - Calling a VTable Based COM Interface document.

2. Using org.jawin.DispatchPtr to call a scriptable COM component

As shown in the PowerPoint demo from the introduction to Jawin, using the IDispatch interface on any scriptable COM object is relatively straightforward:

  1. Create the DispatchPtr object, using either the ProgId or the CLSID for the relevant COM object.
  2. Calling relevant methods on the object - these falls in three groups:

The following subsections are dedicated to how to use each of these three groups of methods on the DispathPtr-class.

Note that COM requires that all threads calling a COM object must initialize the COM library. This is done by calling:

Ole32.CoInitialize();

After a thread is finished with all COM calls, it should call:

Ole32.CoUninitialize();

Microsoft MSDN documents the details of these two methods; CoInitializeEx and CoUninitialize.

It should be noted that COM has a special thread model. So if using COM objects in multithreaded applications, you unfortunately have to know at least the basic of this. So please see the section on Threading Issues if using COM objects from several threads.

When finished with the dispatch interface object, the close() method must be called to let COM decrease the reference count for the COM-object, and ultimately destroy the object. This call must happen on the same thread that created the interface object. And even though the DispatchPtr contains a finalize-method that tries to release the resources if close() was not explicitely called, this can not be relied on, since Java neither guarentees that finalize() will ever be called, nor is it guarenteed from which thread it will be called.

2.1. Get properties

The getters are straightforward, as the caller only needs to specify the property to retrieve (and additional index(es) for identifying the exact property value). DispatchPtr offers three methods for this (please refer to the javadoc for additional information about the specific parameters).

public Object get(String prop) throws COMException;
public Object get(String prop, Object index) throws COMException;
public Object getN(String prop, Object[] indexes) throws COMException;

After invoking one of these methods the caller only needs to cast the result to the correct type (and retrieve the simple type if suitable). The javadoc for Variant.ReadObject() specifies how native Variant types are converted into Java types - for most developers there should be no surprises in conversion (ie. native int is converted to java.lang.Integer, etc.).

2.2. Set properties

To set a property value should be just as straightforward as getting a property value. Again there are three groups of setter-methods, for setting properties taking different numbers of indexes for identifying the property.

public void put(String prop, Object val) throws COMException;
public void put(String prop, Object index, Object val) throws COMException;
public void putN(String prop, Object[] indexes, Object val) throws COMException;

Because of Java's support for overloaded methods a number of shortcut methods also exists for setting properties of simple types, eg. the following method for setting a 4-byte signed integer (a socalled VT_I4):

public void put(String prop, int val) throws COMException;

If you need detailed control of how Jawin converts Java types into native Variants the javadoc for Variant.WriteObject() must be consulted. But please notice that the IDispatch coercion mechanism handles scenarios where the VT_TYPE of a parameter is a not a 100% fit to a method signature or property. Therefore there should not be any problems by setting eg. a property requering unsigned int's with either a long or an int.

2.3. Invoking methods

If you got this far, you can probably guess that invoking methods on the dispatch interface is mostly just a combination of getting and setting properties, that is passing some values to a method as java Objects, and using any return value as another java Object.

Again the javadoc for respectively Variant.WriteObject() and Variant.ReadObject() should be consulted for details about marshalling and unmarshalling of parameters and return values.

2.3.1. [out] parameters

There is one special case that needs a few additional words, the handling of socalled [out] parameters. [out] parameters are usually used when a method returns several values (ie. the return value is "not enough"). An example of such a method could be:

int doSomething([in] int inValue, [out] int outValue);

This method "returns" two values: both the usual return value and the value in the outValue parameter.

To use such a method from Jawin, one have to use the org.jawin.Variant.ByrefHolder class (the name originates from the term passing by reference instead of the usual by value). The example below shows the usage of a ByrefHolder wrapping object. To call the above defined doSomething method, the Java code should be like:

..
// the usual in value
Integer inValue = new Integer(42);
// wrap the out value in a ByrefHolder
Variant.ByrefHolder outValueRef = new Variant.ByrefHolder(new Integer(0));

// call the method
Integer result = (Integer)dispatchPtr.invoke("doSomething", inValue, outValueRef);

// unwrap the outValue
Integer outValue = (Integer)outValueRef.getRef();
..

It should be noted in the above code, that one has to pass an "empty" object of the correct type to the ByrefHolder constructor. This is necessary to get the byref value marshalled into the correct VT_TYPE (in the example above our "empty" value, is the new Integer(0)).

2.3.2. Array parameters

When working with arrays in a dispatch interface, one has to know in some detail how the different java-arrays get marshalled to native Variants. Especially it is important to understand the difference between arrays of simple types (like int[]) and arrays of objects (like Integer[]). The rules are as follows:

Java ArrayVariant TypeComments
String[] VT_ARRAY | VT_BSTR Special case of the below array of Java objects.
Object[] VT_ARRAY | VT_VARIANT Each of the individual objects are then marshalled as an ordinary Variant, eg. a Java array of Integer-objects are marshalled as VT_ARRAY | VT_VARIANT (VT_UI4). One can pass arrays of different Variant types, eg. the following is possible
  new Object[]{ "some string" , new Integer(42) , Boolean.TRUE }
simple type array (eg. int[])VT_ARRAY | VT for simple typeExamples:
  int[] -> VT_ARRAY | VT_UI4
  double[] -> VT_ARRAY | VT_R8
DispatchPtr[] (or subclass) VT_ARRAY | VT_DISPATCH Another special case of array of Java objects.
UnknownPtr[] (or subclass) VT_ARRAY | VT_UNKNOWN Another special case of array of Java objects.

3. Error Handling

The COM error model uses socalled HRESULT's as return values to indicate failure of a call. Jawin maps this into the Java Exception hierarchy by throwing a org.jawin.COMException on failure. This means that the user can program in standard Java-style, ie. catching exceptions and not having to worry about checking return HRESULT's.

Section 5 in the Jawin Architecture document contains some additional information about the conversion of HRESULT's into COMExceptions.

4. Threading Issues

If using COM references in multithreaded applications, you must make yourself comportable with how Jawin handles the threading issues that are present in COM. This is presented in section 6 in the Jawin Architecture document.

5. Additional Resources

Additional resources when working with scriptable COM objects from Jawin