com4j Annotation Guide

This document explains the details of how the runtime bridges a Java method invocation into a COM method invocation, and how one can use annotations to control this process.

In the most general form, a Java method can be annotated as follows:

@IID(iid)
public interface INTERFACE {
  @VTID(vtid)
  @ReturnValue(index=rindex,inout=rio,type=rt)
  T foo(
    @MarshalAs(t1) T1 param1,
    @MarshalAs(t2) T2 param2,
    ... );
}

IID

The iid parameter of the surrounding interface designates the IID of the COM interface. The method invocation is done against this interface of a COM object.

VTID

The mandatory vtid parameter describes the index of the method in the given interface. The com4j runtime never uses the method name information to decide which COM method to invoke. You can deteminer the virtual-table index by counting methods defined on that interface. For example, IUnknown has 3 methods, so @VTID(3) would designate the first method on an interface derived from IUnknown. IDispatch defines 4 additional methods, so the first method on an interface derived from IDispatch would have @VITD(7).

Using the wrong VTID often causes the JVM to crash, because you end up calling a wrong method (or non-existent method) with a wrong set of parameters. So be careful when you manually tweak this.

rindex

In COM, a return value is usually passed as a parameter by reference. Therefore, when a Java method has a return value, com4j bridges it as a parameter. The optional rindex specifies where this parameter is passed among the real parameters. For example, the following Java method:

@ReturnValue(index=0) Tr foo( T1 t1, T2 t2 )

would be bridged to the following COM method invocation:

HRESULT Foo( [out,retval] Tr* r, T1 t1, T2 t2 );

Similarly, the following Java method:

@ReturnValue(index=1) Tr foo( T1 t1, T2 t2 )

would be bridged to the following COM method invocation:

HRESULT Foo( T1 t1, [out,retval] Tr* r, T2 t2 );

When rindex is omitted, it means that the return value is passed after the last parameter, which is what most COM methods do.

rio

Although rare, a COM method parameter can have [in,out,retval] semantics, which means it takes a value from the caller, modifies it, and returns it as the return value of the method.

Specifying true for rio would achieve this semantics. With this switch turned on, instead of inserting a return value among the parameters, the com4j runtime overloads the designated parameter both as a parameter and a return value. Thus the following Java method:

@ReturnValue(index=1,inout=true) T2 foo( T1 t1, T2 t2 )

would be bridged to the following COM method invocation:

HRESULT Foo( T1 t1, [int,out,retval] T2* t2 );

rt

The optional rt parameter specifies the native return type for this method and the semantics of how the return value is mapped to Java. When omitted, a pre-defined table is used to decide which native type to use from the Java return type.

For possible values, their semantics, and allowed Java types, see the javadoc of NativeType.

t1,t2,...

Parameters can be optionally annotated by the MarshalAs attribute to control how a Java parameter is bound to a parameter of a native type. When omitted, the same pre-defined table is used to decide which native type to use.

NativeType specified for the return type and NativeType specified for parameters sometimes have slightly different semantics. See the javadoc for details.

COM Error and Exception

Consider the following COM method:

[helpstring("get the child object.")]
HRESULT GetItem( [int] int index, [out,retval] IFoo** ppItem );

A COM method not just returns a "conceptual" return value (IFoo*) but also returns a HRESULT. tlbimp always hides HRESULT from Java, thus the above method is bound to:

IFoo GetItem( int index );

When the COM method invocation returns a failure HRESULT, the com4j runtime throws unchecked ComException.

Sometimes a COM method actually uses this HRESULT to return a meaningful value. For example,

[helpstring("count the items and returns it, or return a failure code.")]
HRESULT CountItems();

If you want to access the HRESULT return value, use NativeType.HRESULT as follows, which returns the HRESULT value as a Java int:

@ReturnValue(type=NativeType.HRESULT)
int countItems();