Language:

The Free and Open Productivity Suite
Apache OpenOffice 4.1.3 released

The Ole Bridge

OpenOffice

Contents

  1. Introduction
  2. Requirements
  3. Parts of the bridge
  4. Mapping of types
  5. Using the bridge
  6. Creation of types
  7. JScript issues
  8. Value Objects
  9. Limitation of the dispatch objects
  10. Implementing COM objects with UNO interfaces

1 Introduction

The OLE bridge is a means to allow UNO objects to be accessed in a COM environment, that is to say a COM client can calls an UNO server without having do deal with UNO specific details. Conversely an UNO object can use COM objects and does not have to know anything about COM.

The bridge can be incorporated by different languages as long as the languages support OLE. So far the bridge has proved to work reliable with C++ and JScript. VBScript and Visual Basic still needs some testing and as for all other eligible languages they require testing from the ground up.

As the name "OLE" bridge implies the bridge deals with OLE objects and not with COM objects generally. That means that only "dispatch objects" can be mapped to UNO and UNO objects are only mapped to dispatch objects.

2 Requirements

First of all the system must be able to run both object models. As for COM, it runs on all Windows 95/98, Windows 3.51, Windows 2000 and Windows ME operating systems. There are also ports to Macintosh and Unix and since November 1999 there is a reference implementation for UNIX developed by the Open Group. UNO is available whenever the UDK is installed on a machine or indirectly when an Office is installed. As for the latter, the setup needs to be run to ensure that the initial UNO object is properly registered at the system's registry. Simply copying the executables and libraries won't do.

The bridge depends on some additional services. These are

com.sun.star.script.Invocation,
com.sun.star.script.InvocationAdapterFactory,
com.sun.star.script.Converter
com.sun.star.reflection.CoreReflection,
com.sun.star.beans.Introspection.

3 Parts of the Bridge

In contrast to other language bindings the OLE bridge is based on services. The following paragraphs give an overview of the functionalities which are provided by them.

3.1 Service com.sun.star.bridge.OleBridgeSupplier2

Implementation name: com.sun.star.comp.ole.OleConverter2

The service is able to bridge elements of one environment to elements of another environment. This feature is provided by exposing the interface com.sun.star.bridge.XBridgeSupplier2 which looks like

module com {  module sun {  module star {  module bridge {

[ uik(5F97D2F0-50F8-11d4-83240050-04526AB4),
	ident("XBridgeSupplier2", 1.0 ) ]
interface XBridgeSupplier2: com::sun::star::uno::XInterface
{
    any createBridge( [in] any aModelDepObject,
	              [in] sequence< byte > aProcessId,
		      [in] short nSourceModelType,
		      [in] short nDestModelType )
	raises(	com::sun::star::lang::IllegalArgumentException );

}; }; }; };

As the parameter nSourceModelType and nDestModelType suggest this interface allows the bridging of element of very different models. The constant group ModelDependent specifies the values which can be used with that interface.

module com {  module sun {  module star {  module bridge {
constants ModelDependent
{
    const short UNO = 1;
    const short OLE = 2;
    const short JAVA = 3;
    const short CORBA = 4;
};
}; }; }; };

When using OleBridgeSupplier2 then only the constants UNO and OLE will have effect. The parameter aModelDepObject contains the element that is to be bridged and the return value contains the bridged element.

3.2 Service: com.sun.star.bridge.OleBridgeSupplierVar1

Implementation name: com.sun.star.comp.ole.OleConverterVar1

The name OleBridgeSupplierVar1 is admittedly badly chosen and rather nondescript. Anyway, the "Var1" indicates that the service is a variation of the OleBridgeSupplier service. And in fact the functionality is the same but the way it is achieved is partly different. OleConverterVar1 was developed to optimize performance in a remote client server environment. A scenario could look like this:

A remote UNO object object is to be converted into an OLE object. Because the bridge can only handle UNO objects which expose XInvocation it utilizes the com.sun.star.script.Invocation service that creates an invocation object out of every UNO object or struct. Invocation has to introspect the respective object which is very expensive in terms of function calls. Now consider Invocation to reside on the client and the actual object on a remote server. Obviously there would be a great many network round trips causing a negative performance impact.

OleBridgeSupplier2 is just doing that and hence it is inappropriate for this scenario. OleBridgeSupplierVar1 on the other hand allows the remote creation of Invocation. Since Invocation and the object now reside in the same process the process of creating invocation objects is a lot more efficient.

Another issue concerns the way of how the bridge realizes the IDispatch interface for UNO wrapper objects. An UNO wrapper is an object that contains an UNO object and it implements IDispatch so that the wrapper can be used in an OLE environment. Calls on the wrapper's IDispatch are mapped to calls on the XInvocation of the wrapped object.

OLE objects are used through their IDispatch interface. A function call or accessing a property is twofold. First IDispatch::GetIDsOfNames is called which basicly takes the function or property name and returns an id (DISPID). With the id at hand the IDispatch::Invoke can be called. The Invoke implementation knows by way of the DISPID exactly what function or property it has been called for.

The OleBridgeSupplier2 UNO wrapper checks in GetIDsOfNames whether the object exists or not before it issues a DISPID. The caller knows immediately if the function or property exists. This check is not carried out by OleBridgeSupplierVar1 because it would cause at least one remote call (remember the object is remote). DISPIDs are blindly passed out whenever GetIDsOfNames is being called for a new name. If the DISPID produces an error during the Invoke call then the name is cached, so that the next time GetIDsOfNames is being called the caller receives an appropriate error code.

The OleBridgeSupplierVar1 UNO wrapper has to do some more work in IDispatch::Invoke because it has not obtained any type information yet about the meaning of the current call. The wFlags parameter of IDispatch::Invoke can e.g. consist of a combination of DISPATCH_METHOD and DISPATCH_PROPERTYPUT. In this case the wrapper tries first to call a method on the wrapped UNO object and when this fails it tries to access a property. Moreover after a call to XInvocation failed the wrapper checks whether the name is correct by calling XExactName::getExactName that is provided by the invocation object. During the Invoke call information are gathered which are cached so that whenever the same call is made again the cached information are used to fasten up the process.

3.3 com.sun.star.bridge.OleApplicationRegistration

Implementation name: com.sun.star.comp.ole.OleServer

This service registers a COM class factory when the service is being instantiated and deregisteres it when the service is being destroyed. The class factory constructs an UNO multi service factory within the OLE environment. Services created by this factory are always created in that environment. That means that someone who is using the COM class factory as starting point for an application which incorporates UNO rarely has to deal with the OLE bridge themselves.

The multi service factory which is mapped into the COM environment is the same factory which is passed into the component_getFactory function of the housing DLL.

3.4 com.sun.star.bridge.OleObjectFactory

Implementation name: com.sun.star.comp.ole.OleClient

The OleObjectFactory represents itself as multi service factory by exposing the XMultiServiceFactory interface. It is used to create COM objects. The COM components are specified by their programmatic identifier (ProgId). The COM objects are created in the UNO environment, meaning they are wrapped by objects which implement XInvocation and map calls to IDispatch.

To map an OLE object into the UNO environment you could create the object through any COM mechanism (e.g. CoCreateInstance) and then convert it by means of the service OleBridgeSupplier2. Actually the OleObjectFactory does not do otherwise.

4 Mapping of Types

The bridge maps data from UNO to OLE automation types and vice versa. Because the bridge can be used from different languages there are also some language specific issues to be considered; e.g. JScript has no out parameters. The problem is that the bridge cannot tell what language makes use of it, since on the binary level the communication between interfaces complies with the COM specification, regardless of the language. For more information about JScript and the issues involved see chapter 6.

The table 1 summarizes the possible conversions from OLE Automation types to UNO types. (The names in italic letters are not idl types)

OLE Automation (idl types) UNO (idl types)
boolean boolean
unsigned char byte
[1] double double, float
float float
short short
[1] long long, short, byte
BSTR string
SCODE unsigned long
IUnknown* com.sun.star.script.XInvocation
[2] IDispatch* com.sun.star.script.XInvocation
interface type, original type.
sequence<type>,
out parameter,
in/out parameter
[3] SAFEARRAY(type) sequence<type>
[4] type any
  1. JScript does not use the types float, char and short. If the target UNO function takes one of those types then an appropriate conversion occurs.
  2. The default behaviour of the conversion routine is that dispatch objects are converted into invocation objects (objects implementing XInvocation). The actual COM object is wrapped by an UNO object that implements XInvocation. Calls on XInvocation are mapped to calls on IDispatch.

    If a dispatch object is actually a wrapped UNO object then the original UNO object is extracted. Consider the following JScript (COM environment) code:

    // obj is an uno object
    var obj2= obj.getSomeObject();
    //obj2 is an UNO object which is encapsulated by an UNO wrapper
    obj.putSomeObject( obj2);
    

    When obj2 is passed back into the UNO environment then "obj" receives the original UNO object instead of a COM wrapper object.

    When an UNO object in a COM environment receives an dispatch object in an IDispatch::Invoke call then it acquires type information about the parameter. The information contains the type what the dispatch object should represent in the UNO environment. If an interface other than XInvocation is expected than that specific interface is created by way of the service com.sun.star.script.InvocationAdapterFactory.

    In JScript one uses Array objects for out or in/out parameters (see JScript issues ). Those objects are dispatch objects which are converted into the expected type. Besides, the Array objects are of course used for arrays. If so then they are converted into a Sequence with the proper element type.

    A mapped COM object can only be successfully used through XInvocation if it provides type information through IDispatch::GetTypeInfo.

  3. A SAFEARRAY is converted to sequence<type> where type is the respective UNO type. If the SAFEARRAY has more than one dimension then the anys contain sequences of sequences and so forth; e.g. sequence<sequence<long>>.

  4. Whenever an UNO function requires an any parameter then the caller in the COM environment is not obliged to pass a VARIANT (actually VT_VARIANT | VT_BYREF).

    Instead the actual type can be put into the respective VARIANT argument (DISPPARAMS::rgvarg).

    If the bridge receives an VT_DISPATCH then it depends of the type information of the target UNO function or property what the dispatch object will be converted into (see [2], JScript issues).

Table 2 shows the conversions from UNO types to OLE Automation types.

UNO OLE Automation
boolean boolean
byte unsigned char
double double
float float
short short
unsigned short short
long long
unsigned long long
string BSTR
interfaces IDispatch*
[1] sequence<type> SAFEARRAY(type),
SAFEARRAY(VARIANT)
struct IDispatch*
enum long
char short
[2] type VARIANT
  1. A Sequence which has as element type of a sequence could be regarded as two-dimensional array. The difference is that the element sequences could have varying lengths whereas the elements of a certain dimension of a multidimensional array have the same length. To reflect that, sequences are converted into one-dimensional SAFEARRAYs with VARIANT elements which in turn can contain SAFEARRAYs. This conversion works fine with COM objects which expect those arrays, e.g. COM objects which implement UNO interfaces (see chapter 10). But quite often this will not be the case and if a function argument is a SAFEARRAY of a certain element type and dimension then exactly this argument has to be provided.

    Whenever the bridge determines that a COM object does not implement UNO interfaces then it converts a Sequence into a SAFEARRAY as described by the type information provided through the object's IDispatch interface.

    In case IDispatch::Invoke expects an argument of type SAFEARRAY(VARIANT) then the UNO clients can provide sequence<any> or just sequence<type>, where type means the actual UNO type.

  2. The XBridgeSupplier2::createInstance function returns an any that contains the converted element. If the target environment is OLE than the any contains an unsigned long that is actually a VARIANT*. The VARIANT then contains the converted element and has never the VT_BYREF bit set. Hence it cannot contain another VARIANT.

    When a client uses the COM object in an UNO environment (through XInvocation) and calls a function that requires a VARIANT argument, then the bridge creates the arguments for IDispatch::Invoke as needed. In this case DISPPARAMS::rgvarg would contain a VARIANT with VARTYPE::vt= VT_VARIANT | VT_BYREF. To do this the bridge must use type information which is provided through IDispatch::Invoke.

5 Using the bridge

The bridge is automatically used when someone access the Office through the COM mechanism. This is done by creating the service manager component, that has the ProgId "com.sun.star.ServiceManager". The service manager can then be used to create additional UNO services. There is no explicit mapping from COM to UNO or vice versa necessary. All objects which have been obtained directly or indirectly from the service manager are already COM objects.

The ServiceManager implements the XMultiServiceFactory interface, that is used to create UNO services. Bear in mind though, that ServiceManager is a COM object and hence it is to be accessed through the IDispatch interface.

Example C++:

// create the service manager of OpenOffice
IDispatch* pdispFactory= NULL;
CLSID clsFactory= {0x82154420,0x0FBF,0x11d4,{0x83, 0x13,0x00,0x50,0x04,0x52,0x6A,0xB4}};
hr= CoCreateInstance( clsFactory, NULL, CLSCTX_ALL, __uuidof(IDispatch), (void**)&pdispFactory);
// create the CoreReflection service
OLECHAR* funcName= L"createInstance";
DISPID id;
pdispFactory->GetIDsOfNames( IID_NULL, &funcName, 1, LOCALE_USER_DEFAULT, &id);

VARIANT param1;
VariantInit( &param1);
param1.vt= VT_BSTR;
param1.bstrVal=   SysAllocString( L"com.sun.star.reflection.CoreReflection");
DISPPARAMS dispparams= { &param1, 0, 1, 0}; VARIANT result;
VariantInit( &result);
hr= pdispFactory->Invoke( id, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD,
			  &dispparams, &result, NULL, 0);
IDispatch* pdispCoreReflection= result.pdispVal;
pdispCoreReflection->AddRef();
// do something with the CoreReflection service

Example JScript:

var factory= new ActiveAObject("com.sun.star.ServiceManager");
var coreReflection= factory.createInstance("com.sun.star.reflection.CoreReflection");
// do something with the CoreReflection service

Irrespective of the Office you can map every COM or UNO elements directly by using the com.sun.star.bridge.OleBridgeSupplier2 service. The code to convert an UNO service could look like this:

// convert the service xIntServiceX to a dispatch object
Any any;
IDispatch* pdisp= NULL;
any <<= xIntServiceX;
sal_uInt8 arId[16];
rtl_getGlobalProcessId( arId);
Any target= xSuppl->createBridge( any, Sequence<sal_Int8>( (sal_Int8*)arId, 16), UNO, OLE);
if (target.getValueTypeClass() == TypeClass_UNSIGNED_LONG) 
{
    VARIANT* pVariant = *(VARIANT**)target.getValue();
    pdisp= pVariant->pdispVal;
    pdisp->AddRef()
    VariantClear(pVariant);
    CoTaskMemFree(pVariant);
}

// do something with pdisp (IDispatch*)

COM objects can be converted as long as they support IDispatch:

// create the COM object
hr= CoCreateInstance( COMPONENTS_CLSID, NULL,CLSCTX_ALL,
		      IID_Unknown, (void**) &punk);

// create the service factory
Reference<XInterface>
xint= createRegistryServiceFactory( OUString(L"applicat.rdb"));
Reference<XMultiServiceFactory> factory=
	Reference<XMultiServiceFactory>( xint, UNO_QUERY);

// create the bridge service
Reference< XInterface > xIntSupplier= mgr->createInstance(
				OUString(L"com.sun.star.bridge.OleBridgeSupplier2"));
Reference< XBridgeSupplier2 > xSuppl( xIntSupplier, UNO_QUERY);

Any any;
Variant var;
VariantInit( &var);
var.vt= VT_UNKNOWN;
var.punkVal= punk;
any <<= ( sal_uInt32)&var;
sal_uInt8 arId[16];
rtl_getGlobalProcessId( arId);
Any target= xSuppl->createBridge( any, Sequence<sal_Int8>(
				 (sal_Int8*)arId, 16), OLE, UNO );
Reference<XInvocation> theObject;
target>>= theObject;

// theObject contains now the mapped COM object

Apart from objects one can map all the data types as shown in table 1 and table 2.

A "specialization" of the OleBridgeSupplier2 service is the OleObjectFactory service. It does basically what the above source sample does; just mapping a COM object to XInvocation.

6 Creation of types

The creation of types of one environment within the same environment is not an issue but it looks different if this is done in another environment. Usually it is not necessary at all because types of one environment are automatically mapped to types of the other environment. In other words, one creates a value of a type which is native to the current environment and passes it to an object function, where the object stems from the other environment. The bridge automatically converts the type if necessary. This even works for UNO interfaces on COM side (see chapter 10). But it is a different matter with UNO structs. When a struct is mapped from UNO to COM then it is encapsulated by an dispatch object and when this object is passed back to UNO then the original struct is extracted. If an UNO object function takes a struct as argument, then it it not possible to create a dispatch object and pass it as the respective argument. This functionality has not been implemented yet. Instead there are two ways of creating structs in the COM environment. First one can make use of the com.sun.star.CoreReflection service:

// create the service manager of OpenOffice
IDispatch* pdispFactory= NULL;
CLSID clsFactory= {0x82154420,0x0FBF,0x11d4,{0x83, 0x13,0x00,0x50,0x04,0x52,0x6A,0xB4}};
hr= CoCreateInstance( clsFactory, NULL, CLSCTX_ALL, __uuidof(IDispatch), (void**)&pdispFactory);

// create the CoreReflection service
OLECHAR* funcName= L"createInstance";
DISPID id;
pdispFactory->GetIDsOfNames( IID_NULL, &funcName, 1, LOCALE_USER_DEFAULT, &id);

VARIANT param1;
VariantInit( &param1);
param1.vt= VT_BSTR;
param1.bstrVal=   SysAllocString( L"com.sun.star.reflection.CoreReflection");
DISPPARAMS dispparams= { &param1, 0, 1, 0}; VARIANT result;
VariantInit( &result);
hr= pdispFactory->Invoke( id, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD,
			  &dispparams, &result, NULL, 0);
IDispatch* pdispCoreReflection= result.pdispVal;
pdispCoreReflection->AddRef();
VariantClear( &result);

// create the struct's idl class object
OLECHAR* strforName= L"forName";
hr= pdispCoreReflection->GetIDsOfNames( IID_NULL, &strforName, 1, LOCALE_USER_DEFAULT, &id);
VariantClear( &param1);
param1.vt= VT_BSTR;
param1.bstrVal= SysAllocString(L"com.sun.star.beans.PropertyValue");
hr= pdispCoreReflection->Invoke( id, IID_NULL, LOCALE_USER_DEFAULT,
				DISPATCH_METHOD, &dispparams, &result, NULL, 0);

IDispatch* pdispClass= result.pdispVal;
pdispClass->AddRef();
VariantClear( &result);

// create the struct
OLECHAR* strcreateObject= L"createObject";
hr= pdispClass->GetIDsOfNames( IID_NULL,&strcreateObject, 1, LOCALE_USER_DEFAULT, &id)

IDispatch* pdispPropertyValue= NULL;
VariantClear( &param1);
param1.vt= VT_DISPATCH | VT_BYREF;
param1.ppdispVal= &pdispPropertyValue;
hr= pdispClass->Invoke( id, IID_NULL, LOCALE_USER_DEFAULT,
		DISPATCH_METHOD, &dispparams, NULL, NULL, 0);

// do something with the struct pdispPropertyValue


pdispPropertyValue->Release();
pdispClass->Release();
pdispCoreReflection->Release();
pdispFactory->Release();

A simpler way is to use the _GetStruct function which every UNO object in a COM environment provides.

// object be some UNO object in a  COM environment
OLECHAR* strstructFunc= L"_GetStruct";
hr= object->GetIDsOfNames( IID_NULL, &strstructFunc, 1, LOCALE_USER_DEFAULT, &id);

VariantClear(&result);
VariantClear( &param1);
param1.vt= VT_BSTR;
param1.bstrVal= SysAllocString(
L"com.sun.star.beans.PropertyValue");
hr= object->Invoke( id, IID_NULL,LOCALE_USER_DEFAULT, DISPATCH_METHOD,
			&dispparams, &result, NULL, 0);

IDispatch* pdispPropertyValue= result.pdispVal;
pdispPropertyValue->AddRef();

// do something with the struct pdispPropertyValue

7 JScript issues

JScript does not offer arrays in a way as C or C++ do. Instead it provides the Array object which acts as array but is actually a dispatch object. Therefore an UNO object in the COM environment receives an dispatch object when it is used in JScript and an Array object has been passed into a function of that object.

// JScript
var param= new Array(1,2,3);
// object is an UNO object
object.function( param);

JScript does not has out or in/out parameter. To create those parameters we use Array objects. The value on index 0 (actually property "0") contains the out- value after return of the function.

var out= new Array();
object.functionOut(out);
var value= out[0];

If an in/out parameter is needed then the in-value is set at index[0].

var inout= new Array();
inout[0]=123;
object.functionInOut( inout);
var value= inout[0];

Whenever the bridge has to convert a dispatch object then it first has to find out what the object actually represents (object, array, out parameter, in/out parameter) to do an appropriate conversion. This is done by acquiring type information, which itself is not problematic but in a remote environment it causes additional network traffic.To avoid this one could use Value Objects (see chapter 7). Such an object can be used in place of every other parameter in a COM environment. The script programmer sets the type and value of this object, so that the bridge implementation knows what the value actually stands for.

JScript always uses doubles whenever it encounters floating point values. When a UNO function is called which actually takes a float as parameter, then the bridge does a conversion by using the Windows function VariantChangeType. This could cause a change of the value depending on the format of the number used in the script.

When a UNO function returns a float or has a float as out-parameter then the conversion is put off to JScript. The results of this conversion most certainly does not match the expected value. In a test a UNO function has been called with the number1.234567 where the function expected a float. The function then returned that value as float. JScript receives in this case a value of 1.2345670461654663.

A similar situation exist with integer values. JScript exclusively uses 32 bit integer values. The bridge tries to convert to a smaller type if necessary. This works fine as long as the numbers used in JScript correspond to the value range of the parameter type of the respective UNO function argument.

8 Value Objects

8.1 Motivation

An UNO object used in JavaScript is wrapped by an object that is COM compatible in that it exposes IDispatch. Such wrapper may be referred to as uno wrapper in this document. Every access on a uno wrapper within Java Script causes ultimately a call to IDispatch::GetIDsOfNames and on success IDispatch::Invoke is called. The parameters are passed as VARIANT s and are converted into Any s and a Sequence, in and in/out parameter are treated much differently in the process. The problem now is how to recognise the kind of the parameter because they are all represented by IDispatch objects (VT_DISPATCH). To solve this problem every time an IDispatch is to be converted it is checked what kind of parameter this parameter is expected to be and the conversion goes accordingly. For this it is necessary to acquire type information that is usually provided by the office application. In a remote scenario several network round trips would be required to deliver those information to the client. To prevent a severe performance impact it is desirable to dispense with type information. "ValueObjects" are a means to come close to that requirement.

8.2 The Value Object

A ValueObject is an object that acts as a value of a certain type and can be passed to UNO functions instead of the actual parameter. A ValueObject is passed to the wrapper as IDispatch. The uno wrapper queries every IDispatch parameter for the IJScriptValueObject interface and if that interface is being returned than the wrapper has a means to find out the exact type by using that interface and is not dependent on type information provided by the office application. ValueObjects and other parameters can be used interchangeably.

8.3 Creation

Every UNO object used by JavaScript is capable of delivering ValueObjects. To obtain one imply call _GetValueObject:

// object is some uno wrapper object
var valueObject= object._GetValueObject();

8.4 The Interface of the Value Object

A Value Object is actually an instance of class JScriptValue that implements IDispatch and IJScriptValueObject. The IJScriptValueObject looks like:

MIDL_INTERFACE("e40a2331-3bc1-11d4-8321-005004526ab4")
IJScriptValueObject: public IUnknown
{
    STDMETHOD( Set)( VARIANT type,  VARIANT value)= 0;
    STDMETHOD( Get)( /*out*/ VARIANT *val)= 0;
    STDMETHOD( InitOutParam)()= 0;
    STDMETHOD( InitInOutParam)( VARIANT type, VARIANT value)= 0;
    STDMETHOD( IsOutParam)( /*out*/ VARIANT_BOOL* flag)= 0;
    STDMETHOD( IsInOutParam)( /*out*/VARIANT_BOOL * flag)= 0;
    STDMETHOD( GetValue)( /*out*/ BSTR* type,/*out*/ VARIANT *value)= 0;
};

IJScriptValueObject::Set: Sets a value and specifies its type. The param "type" specifies the type by a string and the parameter "value" contains the value. Usable in JavaScript.

IJScriptValueObject::Get: Delivers the value that is contained in the ValueObject. Usable in JavaScript.

IJScriptValueObject::InitOutParam: Stipulates the ValueObject to represent an out parameter. Usable in JavaScript.

IJScriptValueObject::InitInOutParam: Stipulates the ValueObject to represent an in/out parameter. Usable in JavaScript.

IJScriptValueObject::IsOutParam: Returns a boolean (as out parameter)indicating whether the ValueObject acts as out parameter or not. A return value of true means the object is an out parameter. Not for use in JavaScript.

IJScriptValueObject::IsInOutParam: Returns a boolean (as out parameter) indicating whether the ValueObject acts as in/out parameter or not. A return value of true means the objects is an in/out parameter. Not for use in JavaScript.

IJScriptValvueObject::GetValue: Returns the type and value of the ValueObject as out parameter. Not for use in JavaScript. As seen in the above function descriptions only Get, Set, InitInOutParam and InitOutParam can be used in JavaScript. To be more exact these functions are exposed by IDispatch.

8.5 Supported Types

When setting the ValueObject to a value the type is determined by a string that is passed as first parameter in the Set function. The table below shows what strings are currently accepted (column name).

Name Type TypeClass
char sal_Unicode TypeClass_CHAR
boolean sal_Bool TypeClass_BOOL
byte sal_Int8 TypeClass_BYTE
short sal_Int16 TypeClass_SHORT
unsigned short sal_uInt16 TypeClass_UNSIGNED_SHORT
long sal_Int32 TypeClass_LONG
unsigned long sal_uInt32 TypeClass_UNSIGNED_LONG
string OUString TypeClass_STRING
float float TypeClass_FLOAT
double double TypeClass_DOUBLE
any Any TypeClass_ANY
object XInterface TypeClass_INTERFACE

Sequences are represented by prepending "[]", e.g. []char, [][]byte, [][][]object, etc.

8.6 Usage

  1. Sequence:

    // object is an UNO object
    var value= object._GetValueObject();
    var array= new Array(1,2,3);
    value.Set("[]short",array);
    object.function( value);
    

    The array could also contain ValueObjects if necessary:

    var value1= object._GetValueObject();
    var value2= object._GetValueObject();
    value1.Set("short", 100);
    value2.Set("short", 111);
    var array= new Array();
    array[0]= value1;
    array[1]= value2;
    var allValue= object._GetValueObject();
    allValue.Set("[]short", array);
    object.function( allValue);
    
  2. Out parameter:

    // object is an UNO object
    var value= object._GetValueObject();
    value.InitOutParam();
    object.functionOut( value);
    var out= value.Get();
    
  3. In / out parameter:

    // object is an UNO object
    var value= object._GetValueObject();
    value.InitInOutParam("long", 123);
    object.functionInOut( value);
    var out= value.Get();
    

9 Limitations of the dispatch objects

The dispatch objects provided by the bridge do not support type information. IDispatch::GetTypeInfoCount and IDispatch::GetTypeInfo return E_NOTIMPL. Moreover there are no COM type libraries available and the dispatch objects do not implement IProvideClassInfo as well.

IDispatch::GetIDsOfName has to be called for every name separately. That this one cannot query the ids for several names at a time.

IDispatch::Invoke does not support named arguments nor the pExcepInfo and puArgErr parameter.

10 Implementing COM objects with UNO interfaces

10.1 Intention

It is desirable to be able to build COM components which implement UNO interfaces. Those components could be employed as listeners (COM: event sinks), for numerous broadcasters (COM: event source).

10.2 Terms and Conventions

When speaking of implementing UNO interfaces in COM components, we do not imply the use of interfaces in terms of abstract C++ classes. Instead the components have to implement IDispatch and dispatch method calls to functions that have the same names as the respective UNO interface functions.

Basically all interfaces can be implemented as long as the data type used in the UNO interfaces can be mapped to OLE automation types. The mappings are shown in Table 3. One exception is the XInvocation interface. Because it is itself a scripting interface, it does not make sense to employ it within a COM component. Assuming that someone wants to build a COM component that represents some UNO XInvocation implementation then the properties and functions are to be directly implemented. That is, IDispatch::Invoke dispatches directly to those properties and methods.

XInterface does not need to be implemented.

Because the queryInterface mechanism does not work for COM components with implemented UNO interface, they have to supply a means to proclaim those interfaces. This is achieved by the property "_implementedInterfaces" that holds an array of strings which contain the fully qualified interface names. The property is not needed when only one interface is supported.

10.3 Components in C++

To build a component with C++ one can write the component from scratch or use some kind of framework, where Microsoft's ATL comes in handy.

A basic rule with implementing IDispatch is that all parameters (meaning the actual arguments to the function that is called in turn) are converted into the types that the component expects. Although the OLE Bridge does not just pass arguments with the type VT_VARIANT |VT_BYREF but instead the actual type (see mapping table) it is still good practice to convert parameters within the IDispatch::Invoke implementation.

When using ATL there is no need to bother about this as long as one uses a dual interface. Then the parameters correspond to the types as shown in table ...

A COM component is free to support as many UNO interfaces as it needs to but an ATL component just needs to implement one dual interface. All UNO interface functions have to be put in that dual. In fact there is no other way because ATL can only expose one IDispatch.

UNO Type Class OLE Automation Type as VARTYPE
INTERFACE VT_DISPATCH (IDispatch*)
STRUCT VT_DISPATCH (IDispatch*)
ENUM VT_I4 (long)
BOOLEAN VT_BOOL (VARIANT_BOOL)
STRING VT_BSTR (BSTR)
FLOAT VT_R4 (float)
DOUBLE VT_R8 (double)
CHAR VT_I2 (short)
BYTE VT_UI1 (unsigned char)
SHORT VT_I2 (short)
UNSIGNED_SHORT VT_I2 (short)
LONG VT_I4 (long)
UNSIGNED_LONG VT_I4 (long)
SEQUENCE VT_ARRAY | VT_VARIANT
ANY VT_VARIANT

Table 3

Whenever out or in/out parameter are used then the VARTYPEs above are or'd with VT_BYREF.

E.g. there is an UNO interface with this idl description:

interface XSimple : public com.sun.star.uno.XInterface
{
    void func1( [in] long val, [out] long outVal);
    void func2( [in] sequence< long > val, [out] sequence< long > outVal);
};

The MS IDL description (for an ATL component with a dual interface) would look like:

[
    object,
    uuid(xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx),
    dual,
    helpstring("ISimple Interface"),
    pointer_default(unique)
]
interface ISimple : IDispatch
{
    [id(1), helpstring("method func1")]
		HRESULT func1( [in] long val, [out] long* outVal);
    [id(2), helpstring("method func2")]
		HRESULT func2([in] SAFEARRAY(VARIANT) val,
			[out] SAFEARRAY(VARIANT) * outVal);
};

The property looks like this:

[propget, id(4), helpstring("property_implementedInterfaces")]
	HRESULT	_implementedInterfaces([out, retval] SAFEARRAY(BSTR) *pVal);

10.4 Components in JScript

In JScript one does not have to deal with types. Nevertheless it is important to know how arrays, in/out and out parameter are used because the usage is rather different.

For in/out and out parameter an object with a property "0" is passed.

function inoutFunction( val )
{
    var value= val[0];
    val[0]= 123;
}

function outFunction( val)
{
    val[0]= 123;
}

An Array is passed as out parameter:

function outArray( val )
{
    val[0]= new Array( 1,2,3);
}

If a JScript object should implement several interfaces than the object has to have the property "_implementedInterfaces". Additionally a JScript has to support the property "_environment" which must have the value "JScript".

function AnObject()
{
    this._environment= "JScript";
    this._implementedInterfaces= new Array( "XSimple1",
				"XSimple2","XSimple3");

// the interface functions
this.simple1Func= simple1Func;
this.simple2Func= simple2Func;
this.simple3Func= simple3Func;
}
// XSimple1
function simpleFunc()
{
	...
}
// XSimple2
function simple2Func()
{
	...
}
// XSimple3
function simple3Func()
{
    ...
}
Author: Joachim Lingner ($Date: 2004/11/27 13:07:07 $)
Copyright 2001 Sun Microsystems, Inc., 901 San Antonio Road, Palo Alto, CA 94303 USA.

Apache Software Foundation

Copyright & License | Privacy | Contact Us | Donate | Thanks

Apache and the Apache feather logo are trademarks of The Apache Software Foundation. OpenOffice, OpenOffice.org and the seagull logo are registered trademarks of The Apache Software Foundation. Other names appearing on the site may be trademarks of their respective owners.