Difference between revisions of "UNO automation with a binary (executable)"

From Apache OpenOffice Wiki
Jump to: navigation, search
(Preparing a new Code as a starting Point)
Line 295: Line 295:
  
 
[[Category:Development]]
 
[[Category:Development]]
[[Category:Uno:Cpp]]</code>
+
[[Category:Uno:Cpp]]
 +
[[Category:Uno:Tutorials]]
 +
[[Category:Tutorials]]

Revision as of 17:21, 16 May 2006

We want now discuss a fully UNO/OpenOffice.org program : we mean a program which automates some tasks on OpenOffice.org documents, file loading, file modifying, file saving... all in all “Create a binary program to replace a OOoBasic program”. The border between the examples of previous chapter and of this chapter is vague. I can only say  : if you imagine to automate any problem and create a program for, this paragraph and the next chapters are for you.

Introduction : starting from a SDK example

Danny Brewer wrote in the oooforum.org : “To do almost anything useful with OOo via. the API, in almost all cases, you must either create a new document, or open an existing document. Then you would manipulate the contents of the document, extract information from it, print it, convert it to a different format, work with data forms on the document, or perform various other tasks with office documents.

Therefore, one of the first things to learn is how to open or create documents.

ServiceManager and Desktop objects

In order to work with OOo via. the API, you must first acquire two essential objects.

  1. the ServiceManager
  2. the Desktop

Once you have the ServiceManager, you call its createInstance() method to get the Desktop object. Once you have the Desktop object, you can use it to create or open new documents.

Getting the Service Manager

In every different programming language, there are different mechanisms for acquiring the Service Manager.” One way of getting the service manager is given in one SDK example : <OpenOffice.org1.1_SDK>/examples/DevelopersGuide/ProfUNO/CppBinding We first check the example (under Linux) :

cd <OpenOffice.org1.1_SDK>
./setsdkenv_unix
cd examples/DevelopersGuide/ProfUNO/CppBinding
make
make Office_connect.run

which only writes out "Connected successfully to the office". Under Windows it would be :

cd <OpenOffice.org1.1_SDK>
setsdkenv_windows
cd examples/DevelopersGuide/ProfUNO/CppBinding
make
make Office_connect.run

We start first our discussion from this example. Later on, we will provide an other starting code. All listings we will give below are to be added at this example. Where ? It's shown here (in red) : [cpp] //C++ *** extract from office_connect.cxx int main( ) { // create the initial component context Reference< XComponentContext > rComponentContext = defaultBootstrap_InitialComponentContext(); // retrieve the servicemanager from the context Reference< XMultiComponentFactory > rServiceManager = rComponentContext->getServiceManager(); // instantiate a sample service with the servicemanager. Reference< XInterface > rInstance = rServiceManager->createInstanceWithContext( OUString::createFromAscii("com.sun.star.bridge.UnoUrlResolver" ), rComponentContext ); // Query for the XUnoUrlResolver interface Reference< XUnoUrlResolver > rResolver( rInstance, UNO_QUERY ); if( ! rResolver.is() ) { printf( "Error: Couldn't instantiate com.sun.star.bridge.UnoUrlResolver service\n" ); return 1; } try { // resolve the uno-url rInstance = rResolver->resolve( OUString::createFromAscii( "uno:socket,host=localhost,port=8100;urp;StarOffice.ServiceManager" ) ); if( ! rInstance.is() ) { printf( "StarOffice.ServiceManager is not exported from remote counterpart\n" ); return 1; } // query for the simpler XMultiServiceFactory interface, sufficient for scripting Reference< XMultiServiceFactory > rOfficeServiceManager (rInstance, UNO_QUERY); if( ! rInstance.is() )

       {
           printf( "XMultiServiceFactory interface is not exported for StarOffice.ServiceManager\n" );
           return 1;
       }
       printf( "Connected sucessfully to the office\n" );

// ***** add your code here } catch( Exception &e ) { OString o = OUStringToOString( e.Message, RTL_TEXTENCODING_ASCII_US ); printf( "Error: %s\n", o.pData->buffer ); return 1; } return 0; } I cannot explain all the lines of this source file. I can just say : here is one way among others to get the Service Manager. A second way is given in <OpenOffice.org1.1_SDK>/examples/cpp/DocumentLoader's example. That piece of code is in general called bootstrapping.

Introduction to Bootstrapping

Bootstrap processes are more deeply discussed later. We give only an introduction here.

To memorize this previous piece of code is not easy and probably useless. However, we give a drawing representation of this piece of code to show the main step of the bootstrap process.

Ch4Fig1bootstrap.png

If you are interested in, have a look simultaneously to the code and to the drawing to see the schematic conventions I have used to carry out the Figure above. For instance both XInterface are connected because we use the same variable in fact. The gray filled rectangles are for parameters... and so on.

We want now the desktop and load a OooCalc component.

The first step is to obtain desktop. You then add this code :

[cpp] Reference< XInterface > xDesktop = rOfficeServiceManager->createInstance(

  	OUString::createFromAscii( "com.sun.star.frame.Desktop" ));

Your desktop object is xDesktop.

Second step : query for the XComponentLoader interface

[cpp] // query a XcomponentLoader Interface Reference< XComponentLoader > rComponentLoader (xDesktop, UNO_QUERY); if( rComponentLoader.is() )

   	{
       	printf( "XComonentloader succesfully instanciated\n" );
   	}

Before to see the problems with this code, we give again a schematic representation :

Ch4fig2Componentloader.png

The code above gives error messages :

error 'XcomponentLoader' undeclared (First use this function) ....

It shows us there is a problem with XComponentLoader : this indicates we have probably to add a hpp header file in the program's include statements. The file's name is probably XComponentLoader.hpp but we have to find where it lies. One way is to go in the general index and to look for XComponentLoader. We find : “::com::sun::star::frame” which indicates [cpp]

  1. include <com/sun/star/frame/XComponentLoader.hpp>

is required. We can try and see it is not enough, we obtain always the same error message and one new before : com/sun/star/frame/XComponentLoader.hpp: No such file or directory I have already discussed this fact : the SDK doesn't provide any hpp file, they have to be constructed from IDL files. An other task is then  : open and change the makefile. Only add the correspondant line :

#added com.sun.star.frame.XComponentLoader
TYPES := \
	com.sun.star.uno.XNamingService \
	com.sun.star.uno.XComponentContext \
	com.sun.star.uno.XWeak \
	com.sun.star.uno.XAggregation \
	com.sun.star.frame.XComponentLoader \
	com.sun.star.lang.XMain \ 
        com.sun.star.lang.XMultiServiceFactory \
	com.sun.star.lang.XSingleComponentFactory \
	com.sun.star.lang.XTypeProvider \
	com.sun.star.lang.XComponent \
	com.sun.star.registry.XSimpleRegistry \
	com.sun.star.registry.XImplementationRegistration \
	com.sun.star.bridge.XBridgeFactory \
	com.sun.star.bridge.XUnoUrlResolver \
    com.sun.star.container.XHierarchicalNameAccess

Making the project gives again the same error message. What is still lacking now ? Only a namespace statement in the source file : [cpp] using namespace com::sun::star::frame;

Step 3 : get an instance of the spreadsheet

[cpp] // C++ Reference< XComponent > xcomponent = rComponentLoader->loadComponentFromURL( OUString::createFromAscii("private:factory/scalc"),

       OUString::createFromAscii("_blank"),
       0,
       Sequence < ::com::sun::star::beans::PropertyValue >());

and it works : you have a new OOoCalc document.

This Chapter 10.1 will takkle again the header file construction problem.

An other thing to note is : when constructing variables with Reference template like :

	Reference<...>varName 

you can check with

	varName.is() 

function. The problem of what to do if this variable is not created (varName.is() is false) is complex and not deeply tackled in this document. It refers to exceptions.

The make dependency

Preparing a new Code as a starting Point

The programming style presented in introduction doesn't completely satisfy me. I give it because it's more easy to start explanations from an example provided with the SDK than to start with a new code. I find the functions with Niall Dorgan's code style better in oooforum.org. Then I will adopt it slightly modified : a function is responsible of the connection : ooConnect()

[cpp] // C++

  1. include <stdio.h>
  2. include <cppuhelper/bootstrap.hxx>
  3. include <com/sun/star/bridge/XUnoUrlResolver.hpp>
  4. include <com/sun/star/lang/XMultiServiceFactory.hpp>

// added

  1. include <com/sun/star/frame/XComponentLoader.hpp>

using namespace com::sun::star::uno; using namespace com::sun::star::lang; using namespace com::sun::star::bridge; // added using namespace com::sun::star::frame;

using namespace rtl; using namespace cppu;

// a procedure for what the so called boostrap Reference< XMultiServiceFactory > ooConnect(){

  // create the initial component context
  Reference< XComponentContext > rComponentContext = 

defaultBootstrap_InitialComponentContext();

  // retrieve the servicemanager from the context
  Reference< XMultiComponentFactory > rServiceManager = 

rComponentContext->getServiceManager();

  // instantiate a sample service with the servicemanager.
  Reference< XInterface > rInstance =  rServiceManager->createInstanceWithContext(
        OUString::createFromAscii("com.sun.star.bridge.UnoUrlResolver" ),rComponentContext );
  // Query for the XUnoUrlResolver interface
  Reference< XUnoUrlResolver > rResolver( rInstance, UNO_QUERY );
  if( ! rResolver.is() ){
     printf( "Error: Couldn't instantiate com.sun.star.bridge.UnoUrlResolver service\n" );
     return NULL;
  }
  try {
     // resolve the uno-url
     rInstance = rResolver->resolve( OUString::createFromAscii(
        "uno:socket,host=localhost,port=8100;urp;StarOffice.ServiceManager" ) );
  
     if( ! rInstance.is() ){
        printf( "StarOffice.ServiceManager is not exported from remote counterpart\n" );
        return NULL;
     }
     // query for the simpler XMultiServiceFactory interface, sufficient for scripting
     Reference< XMultiServiceFactory > rOfficeServiceManager (rInstance, UNO_QUERY);
     if( ! rOfficeServiceManager.is() ){
           printf( "XMultiServiceFactory interface is not exported for

StarOffice.ServiceManager\n" );

           return NULL;
       }       
       return rOfficeServiceManager;
  }
  catch( Exception &e ){
     OString o = OUStringToOString( e.Message, RTL_TEXTENCODING_ASCII_US );
     printf( "Error: %s\n", o.pData->buffer );
     return NULL;
  }
  return NULL;

}

int main( ) { //retrieve an instance of the remote service manager

   Reference< XMultiServiceFactory > rOfficeServiceManager;
   rOfficeServiceManager = ooConnect();
   if( rOfficeServiceManager.is() ){
       printf( "Connected sucessfully to the office\n" );
   }

//get the desktop service using createInstance returns an XInterface type

   Reference< XInterface  > Desktop = rOfficeServiceManager->createInstance(
   OUString::createFromAscii( "com.sun.star.frame.Desktop" ));

//query for the XComponentLoader interface

   Reference< XComponentLoader > rComponentLoader (Desktop, UNO_QUERY);
   if( rComponentLoader.is() ){
       	printf( "XComponentloader successfully instanciated\n" );
   	}

//get an instance of the spreadsheet

   Reference< XComponent > xcomponent = rComponentLoader->loadComponentFromURL(

OUString::createFromAscii("private:factory/scalc"),

       OUString::createFromAscii("_blank"),
       0,
       Sequence < ::com::sun::star::beans::PropertyValue >());

// add code here

   return 0;

} Put the above code in office_connect.cxx file in the <OpenOffice.org1.1_SDK>/examples/DevelopersGuide/ProfUNO/CppBinding directory (renaming and storing the old office_connect.cxx) and you can use the slightly modified makefile of previous introduction to compile the project. From now on, we will use this code as starting point. It means I will give only part of this code in the examples : the main() or part of the main.

See also

Personal tools