Automation Objects with UNO Interfaces

From Apache OpenOffice Wiki
Jump to: navigation, search



It is common that UNO functions take interfaces as arguments. As discussed in section Usage of Types, those objects are usually obtained as return values of UNO functions. With the Automation bridge, it is possible to implement those objects even as Automation objects and use them as arguments, just like UNO objects.

Although Automation objects can act as UNO objects, they are still not fully functional UNO components. That is, they cannot be created by means of the service manager. Also, there is no mapping of UNO exceptions defined. That is, an UNO object implemented as automation object cannot make use of exceptions nor can it convey them in any other way.

One use case for such objects are listeners. For example, if a client wants to know when a writer document is being closed, it can register the listener object with the document, so that it will be notified when the document is closing.

Requirements

Automation objects implement the IDispatch interface, and all function calls and property operations go through this interface. We imply that all interface functions are accessed through the dispatch interface when there is mention of an Automation object implementing UNO interfaces. That is, the Automation object still implements IDispatch only.

Basically, all UNO interfaces can be implemented as long as the data types used with the functions can be mapped to Automation types. The bridge needs to know what UNO interfaces are supported by an Automation object, so that it can create a UNO object that implements all those interfaces. This is done by requiring the Automation objects to support the property Bridge_implementedInterfaces, which is an array of strings. Each of the strings is a fully qualified name of an implemented interface. If an Automation object only implements one UNO interface, then it does not need to support that property.

Documentation note.png You never implement com.sun.star.script.XInvocation and com.sun.star.uno.XInterface. XInvocation cannot be implemented, because the bridge already maps IDispatch to XInvocation internally. Imagine a function that takes an XInvocation:
  :// UNO IDL 
  :void func( [in] com.sun.star.script.XInvocation obj);

In this case, use any Automation object as argument. When an interface has this function,

  :void func( [in] com.sun.star.XSomething obj)

the automation object must implement the functions of XSomething, so that they can be called through IDispatch::Invoke.

Examples

The following example shows how a UNO interface is implemented in VB. It is about a listener that gets notified when a writer document is being closed.

To rebuild the project use the wizard for an ActiveX dll and put this code in the class module. The component implements the com.sun.star.lang.XEventListener interface.

  Option Explicit
  Private interfaces(0) As String
 
  Public Property Get Bridge_ImplementedInterfaces() As Variant
      Bridge_ImplementedInterfaces = interfaces
  End Property
 
  Private Sub Class_Initialize()
      interfaces(0) = "com.sun.star.lang.XEventListener"
  End Sub
 
  Private Sub Class_Terminate()
      On Error Resume Next
      Debug.Print "Terminate VBEventListener"
  End Sub
 
  Public Sub disposing(ByVal source As Object)
      MsgBox "disposing called"
  End Sub

You can use these components in VB like this:

  Dim objServiceManager As Object
  Dim objDesktop As Object
  Dim objDocument As Object
  Dim objEventListener As Object
 
  Set objServiceManager= CreateObject("com.sun.star.ServiceManager")
  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 the event listener ActiveX component
  Set objEventListener= CreateObject("VBasicEventListener.VBEventListener")
 
  'register the listener with the document
  objDocument.addEventListener objEventlistener

The next example shows a JScript implementation of a UNO interface and its usage from JScript. To use JScript with UNO, a method had to be determined to realize arrays and out parameters. Presently, if a UNO object makes a call to a JScript object, the bridge must be aware that it has to convert arguments according to the JScript requirements. Therefore, the bridge must know that one calls a JScript component, but the bridge is not capable of finding out what language was used. The programmer has to provide hints, by implementing a property with the name "_environment" that has the value "JScript".

  // UNO IDL: the interface to be implemented
  interface XSimple : public com.sun.star.uno.XInterface
  {
      void func1( [in] long val, [out] long outVal);
      long func2( [in] sequence< long > val, [out] sequence< long > outVal);
      void func3( [inout]long);
  };
  // JScript: implementation of XSimple
  function XSimplImpl()
  {
      this._environment= "JScript";
      this.Bridge_implementedInterfaces= new Array( "XSimple"); 
 
      // the interface functions
      this.func1= func1_impl;
      this.func2= func2_impl;
      this.func3= func3_impl;
  }
 
  function func1_impl( inval, outval)
  {
      //outval is an array
      outval[0]= 10;
      ...
  }
 
  function func2_impl(inArray, outArray)
  {
      outArray[0]= inArray;
      // or 
      outArray[0]= new Array(1,2,3);
 
      return 10;
  }
 
  function func3_impl(inoutval)
  {
      var val= inoutval[0];
      inoutval[0]= val+1;
  }

Assume there is a UNO object that implements the following interface function:

  //UNO IDL
  void doSomething( [in] XSimple);

Now, call this function in JScript and provide a JScript implementation of XSimple:

  var factory= new ActiveXObject("com.sun.star.ServiceManager");
  // create the UNO component that implements an interface with the doSomething function 
  var oletest= factory.createInstance("oletest.OleTest");
  oletest.doSomething( new XSimpleImpl());
  ...

To build a component with C++, write the component from scratch or use a kind of framework, such as the Active Template Library (ATL). When a dual interface is used with ATL, the implementation of IDispatch is completely hidden and the functions must be implemented as if they were an ordinary custom interface, that is, use specific types as arguments instead of VARIANTs. If a UNO function has a return value, then it has to be specified as the first argument which is flagged as "retval".

  // UNO IDL
  interface XSimple : public com.sun.star.uno.XInterface
  {
      void func1( [in] long val, [out] long outVal);
      long func2( [in] sequence< long > val, [out] sequence< long > outVal);
  };
 
  //IDL of ATL component
  [
      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([out,retval] long ret, [in] SAFEARRAY(VARIANT) val, 
                          [out] SAFEARRAY(VARIANT) * outVal);
      [propget, id(4), helpstring("property_implementedInterfaces")] 
          HRESULT Bridge_implementedInterfaces([out, retval] SAFEARRAY(BSTR) *pVal);
  };
Content on this page is licensed under the Public Documentation License (PDL).
Personal tools
In other languages