This part of the userguide, covers how to use Jawin when calling standard COM interfaces, where the functions are invoked through vtable entries. 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.
If you are working with a so called "scriptable" COM object - an object with a dispatch or dual interface - please see the documentation for dispatch based COM interfaces, as these are generally simpler to work with.
The COM dictionary is full of cryptic terms and abbrevations that one must know a minimum of to use Jawin succesfully. A couple of these are explained in the sections below.
GUID is an abbrevation of Globally Unique Identifier, that
is a unique 128-bit (16 bytes) integer used for identifying all kind
of objects and interfaces in the COM world. Both CLSID's and IID's are
instances of GUID's. A GUID is typically written as hex digits on the form
{4 bytes - 2 bytes - 2 bytes - 2 bytes - 6 bytes}
, eg. like
{6EFEB125-55E2-4D6D-A17A-A2F038A647B2}
.
A CLSID is a GUID identifying a COM class (a class id). Microsoft MSDN contains the following information about CLSID's. When creating a COM object use the CLSID to tell the COM libraries what type of object to create.
IID is an abbrevation of Interface Identifier. This is
a GUID identifying a COM interface. It is important to distinguish
between a COM interface and a COM class or object. One COM object typically
implements several COM interfaces, so when using a COM object, one must
both specify an identifier for the object to use (typically a CLSID or
a ProgID, see later) and an identifier for the actual interface on the
object (by specifying a IID). DIID is another abbrevation for
interface identifer, and is used for interfaces implementing the
IDispatch
interface, i.e. a Dispatch Interface Identifier.
Since GUID's are hard to read (and remember) for most normal programmers,
one can use a ProgID, or programmatic identifier instead of
a CLSID for identifying a COM class. Microsoft MSDN contains the following
information about
ProgID's.
ProgIDs are on the form <Program>.<Component>[.<Version>]
,
eg. like CallCOMUnitTest.DualTest.1
.
To use Jawin to call methods in an interface on a COM object, the
class org.jawin.COMPtr
should in broad terms be used
the following way:
COMPtr
-class should be extended and registered
with Jawin for each requested COM interface on the COM object.
comInvoke()
-method on the created instance. Since
this is a type-less method for generic calls, the recommended way
to extend the COMPtr
class, is, for each method
in the COM interface, to make a delegator method, adding type-safety
and hiding the complexity of calling the comInvoke()
method (this is way the Jawin Type Browser generates stub code and
makes it a lot easier for client code to use the 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 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 COMPtr
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.
To show how to use a vtable based COM interface the following sections
will be a code walk-through, using the IDualTest
interface
used in the Jawin unit-tests (this is supplied in
bin/CallCOMUnitTestD.dll
in the source-release.
It is not included in the binary release). The properties and methods
of the IDualTest
can be found by inspecting the interface
definition in cpp/CallCOMUnitTest/CallCOMUnitTest.idl
.
Note that the IDualTest
interface is actually a socalled
dual-interface implementing the IDispatch
interface.
Therefore it is a lot easier to use this interface by following the
guidelines in dispatch
based COM interfaces. But as a matter of example we use the vtable
based interface on this dual interface.
org.jawin.COMPtr
and registering the COM interface in Jawin
All used COM vtable based interfaces have to be registered in Jawin
before use. The standard way to do this, is by making a new Java class
for each used COM interface. This class should extend the
COMPtr
-class and follow the guide lines mentioned in the
javadoc API documentation for the COMPtr
class.
This looks like the following for the IDualTest
interface.
import org.jawin.COMPtr;
import org.jawin.GUID;
import org.jawin.IdentityManager;
/**
* The class extends COMPtr.
*/
public class IDualTestItf extends COMPtr {
/**
* The Interface Identifier (IID) for the IDualTest interface.
*/
public static final GUID IID =
new GUID("{6EFEB125-55E2-4D6D-A17A-A2F038A647B2}");
public static final int IID_TOKEN;
static {
// register the IID in Jawin on class loading time
IID_TOKEN = IdentityManager.registerProxy(IID, IDualTestItf.class);
}
/**
* Implementation of the required getIIDToken() method.
*/
public int getIIDToken() {
return IID_TOKEN;
}
/**
* The required public no arg constructor.
*
* Important:Should never be used as this creates an uninitialized
* IDualTestItf (it is required by Jawin for some internal working though).
*/
public IDualTestItf() {
super();
}
/**
* For creating a new COM-object with the given progid and with
* the IDualTest interface.
*
* @param progid the progid of the COM-object to create.
*/
public IDualTestItf(String progid) throws COMException {
super(progid, IID);
}
/**
* For creating a new COM-object with the given clsid and with
* the IDualTest interface.
*
* @param clsid the GUID of the COM-object to create.
*/
public IDualTestItf(GUID clsid) throws COMException {
super(clsid, IID);
}
/**
* For getting the IDualTest interface on an existing COM-object.
* This is an alternative to calling {@link #queryInterface(Class)}
* on comObject.
*
* @param comObject the COM-object to get the IDualTest interface on.
*/
public IDualTestItf(COMPtr comObject) throws COMException {
super(comObject);
}
}
IDualTestItf
-class
The first step before using the IDualTestItf
-class, is to
call Ole32.CoInitialize()
like mentioned above. After the
COM library has been initialized by this call, it is possible to create
a COM object implementing the IDualTest interface, by using either the
ProgID or CLSID constructor:
..
Ole32.CoInitialize();
// using the ProgID constructor
IDualTestItf dualTestObj = new IDualTestItf("CallCOMUnitTest.DualTest");
// call methods on dualTestObj
..
// and finally release dualTestObj - this could be embedded in a finally clause
dualTestObj.close();
Ole32.CoUninitialize();
..
or using the CLSID constructor. Note that the CLSID is NOT the same as
the IID registered in the IDualTestItf
-class:
..
// using the CLSID constructor
GUID clsid = new GUID("{F420726D-905D-4D69-A225-A908152B2951}");
IDualTestItf dualTestObj = new IDualTestItf(clsid);
..
DualTest
interface
After one has gotten a reference to the wanted COM object the methods
on the COM object is called by using one of the generic
comInvoke()
methods (the second one is the recommended method):
public byte[] comInvoke(int vtable, String instructions, int stackSize,
int argStreamSize, byte[] argStream, Object[] objectArgs)
throws COMException;
public byte[] comInvoke(int vtable, String instructions, int stackSize,
NakedByteStream argStream, Object[] objectArgs)
throws COMException;
The recipe for calling a native COM method is along the lines:
vtable
argument in either of the comInvoke
-methods.
instructions
-string, for the stackSize
and
for the argStream
-parameters - don't worry if this seems
unclear, further explanation follows below).
comInvoke
method with the vtable-identifer
and the serialized parameters for the native method. This will
do the following on the native side:
The two steps of this, that needs further explanation are the serialize and deserialize steps (i.e. step 2 and 4) together with the instruction string.
comInvoke
Since Windows uses little-endian byte order internal, the serialization
of Java variables onto the byte-array that should be passed to the
native code, must use this byte order to. To assist in this, the
caller should use the org.jawin.io.LittleEndianOutputStream
-
class. The recommended way to do this is like:
org.jawin.io.NakedByteStream
-object, which is just a
simple subclass to java.io.ByteArrayOutputStream
, that
allows access without copying to the wrapped byte array. This should
be used for better performance. Note: be aware of the limitations
of working with the internal byte-array, that is NEVER use the
.length
value of the internal array, instead use the
value of the size()
method on the NakedByteStream
.org.jawin.io.LittleEndianOutputStream
object,
passing the NakedByteStream
-object to the constructor.LittleEndianOutputStream
for each variable.
After this, the NakedByteStream
contains the serialized
variables in little-endian byte order, and can then be passed to the
comInvoke()
method.
A code snipped, showing an example of the above would be like
..
// first create the NakedByteStream
NakedByteStream nbs = new NakedByteStream();
// wrap it in a LittleEndianOutputStream
LittleEndianOutputStream leos = new LittleEndianOutputStream(nbs);
// then serialize two integers.
leos.writeInt(value1);
leos.writeInt(value2);
..
For Jawin to be able to deserialize the byte array correctly on the
native side, it needs meta-instructions for what the array contains.
This is what the instructions
and stackSize
parameters are for.
__cdecl
, __stdcall
and __fastcall
)
dictates that all smaller than 4 bytes values (32 bit), are widened to
4 bytes. Therefore the size of short, byte, char and boolean should be
specified as 4 bytes, opposed to what one would belive.XXX:Y:ZZZ
, where XXX
is
directions for [in], Y
is [retval] and ZZZ
is [out]-directions. If no directions is present for a specific section,
the section can be empty, but the colons can not be left out, eg. a
instruction string for a method with no [out] parameters should be on
the form XXX:Y:
(note the trailing ':').
TODO insert reference to a specification for the different instruction strings-values.
comInvoke
The comInvoke
method returns any [retval] and [out]
values in a new byte array. This should then be deserialized into
Java types (just the opposite of the serializing in section 3.3.1)
by using a org.jawin.io.LittleEndianInputStream
like
in this sample (with one integer [return] val, and one [out]-integer)
..
byte[] result = comPtr.comInvoke(..);
// wrap result in a LittleEndianInputStream
LittleEndianInputStream leis = new LittleEndianInputStream(new ByteArrayInputStream(result));
// any [retval] values are placed first
int retVal = leis.readInt();
// and then follows any [out] values
int outVal = leis.readInt();
..
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
COMException
s.
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.
Additional resources when working with vtable based COM objects from Jawin
Copyright (c) 2000-2005, The Jawin Project - LICENSE | |
This documentation was written for version: 2.0, alpha 1 of Jawin. | |
$Id: jawinuserguide_vtable.html,v 1.9 2004/08/21 20:23:15 arosii_moa Exp $ |