Difference between revisions of "Constructing Components"

From Apache OpenOffice Wiki
Jump to: navigation, search
m (Using an Helper to construct the scriptable Counter)
m (Using an Helper to construct the scriptable Counter)
Line 506: Line 506:
 
and we can use the makefile of this component.  
 
and we can use the makefile of this component.  
  
{{Documentation/Caution|For a try I use the first solution but never make it working. There was probably a problem with registery. If a second program manage registry as in the counter example, I am unable to registery.
+
{{Documentation/Caution|For a try I use the first solution but never make it working. There was probably a problem with registery. If a second program manage registry as in the counter example, I am unable to see the component correctly.
  
 
It works when using the makefile of CppComponent renaming the IDL file and cxx file in Makefile as mentioned below.  
 
It works when using the makefile of CppComponent renaming the IDL file and cxx file in Makefile as mentioned below.  

Revision as of 15:40, 25 May 2009

Our purpose is to extend OpenOffice.org in any way. All the possibilities of extensions come not in spirit at the first time you ask yourself how is it possible ? But at first, if I say you : realize first a (shared/not shared) library and second says OpenOffice.org you have to use this library, are you shocked ? Not at all I suppose. Then we have to produce a shared library with suffix uno.so under Linux or uno.dll under Windows. To indicate OpenOffice.org to use this new library and where to find it, we use a rdb file and an operation called registry. Registry has been already tackled in UNO registery and Bootstrapping, but we have to go further in this chapter.

Danny Brewer's Terminology

You can read first in this wiki the extensions terminology. We can also read here :

Let's clarify (and perhaps even invent) some terminology.

  • Add On

A menu item on the Tools menu, and an icon on the toolbar. By modifying the Configuration sub node for AddOn, you can add new menu items and toolbar icon items to the AddOn menu or toolbar icon. Basic programs can modify the Configuration to add some Add On items to the menu. It is not necessary to use pkgchk. Components (see below) can also add things to the AddOn menu or toolbar when the component is installed using pkgchk.

  • Add In

A component (see below) which provides new Calc spreadsheet functions. These functions show up on the Function List and Insert --> Function... dialog box. The dialog box can then fully describe your functions, their parameters, provide tooltips, etc.

An UNO Component, written in any supported programming language, which must be installed using the pkgchk command. (This would be "pkgchk.exe" for Windows users.) A component provides one or more new Services which are registered in the OOo registry. These services can be instantiated just like the factory installed services in OOo. For instance, in Basic, you can just call createUnoService( "name.DannyBrewer.magic.SomeService" ) to get an instance of the service, and then immediately call its methods or properties. Similarly, Python or Java, or Visual Basic, or any other language can use the newly installed service.

A component provides one or more services. See for instance Using Java Inspector component.

  • Service

An UNO abstraction for an Object. A service may not actually represent a single underlying object. A service may have many different interfaces which it implements. A service has properties. What methods are callable on the service are determined purely by what interfaces the service implements.

Since a Calc Add-In is just a component, which implements certain particular interfaces and services, the Calc AddIn is installed and registered in the same way as any other component. That is, a Calc AddIn is installed or removed by using pkgchk. Making a Calc Add-In is just like making any other service. You must be a particular com.sun.star.sheet.AddIn service, and must properly implement all of its interfaces, of which there are several. But once you do this, your service magically provides new functions to the spreadsheet. Calc Add-In are tackled the next chapter. You can find an introduction on addIn also here.

Compilation Chain of a Component

To create a UNO component, you require an IDL file and a cpp file. The entire process is illustrated in the figure below. The final goal is to create a uno.so and a rdb file. If you want more information on binary tools in the compilation chain, have a look at the corresponding chapter in Developer's Guide.


ComponentTools.png

The black arrows in the figure above indicate how to create one file type from another. For example, use idlc to create some.urd starting from some.idl. The red arrow indicates that regcomp is used to place the name of the library done into the rdb file rather than create the rdb file. Finally, the blue arrows indicate a dependency. The entire process is as follows :

  1. Create the idl and cpp files. Figure above shows these files as some.idl and some.cpp.
  2. Use idlc to compile the idl file to a urd file.
  3. Transform the urd file to the rdb file using regmerge. The rdb file is not yet complete because it must contain the name of the uno.so file, which is added at the end using regcomp.
  4. Use cppumaker to create the hpp and hdl header files.
  5. Use gcc to compile the cpp file.
  6. The final registry step has been discussed previously and is not shown in Figure above.

Template:Documentation/Note

To create a component, you must first create an idl file. The IDL file describes the interface. The goal of the following examples, is to create a component, visible to OpenOffice.org more specifically, visible from OOoBasic. The SDK contains an example in: “<OpenOffice.org1.1_SDK>/examples/DevelopersGuide/Components/CppComponent” and also described in Developer's Guide. The view described above is only important when you wonder how a component is constructed. If you want to know what you have to do when writing a component an other view is needed ; it is described later with the corresponding three rules.

First Example : accessing a Counter from OOoBasic

Our first goal is to transform a counter into a component. A counter is perhaps not great but a good start. The inspiration comes to me with the following two SDK's examples :

  • <OpenOffice.org1.1_SDK>/examples/DevelopersGuide/Components/CppComponent (used in previous section) and also descibed in Developer'sGuide.
  • <OpenOffice.org1.1_SDK>/examples/cpp/counter

The problem with the counter example given above with the SDK is its inaccessibility from OOoBasic. A component callable from OOoBasic and then from all other programming language is called scriptable. We begin describing the counter.

A very simple Counter

We give in this section our start : a counter.cxx file and a countermain.cxx file working all together. Please note that this example would be simpler if not constructed for OpenOffice.org. It's only given here to present our start. From this start we will wonder a lot of questions showing I hope how components work. I will use sometimes UML notations in this chapter.

Single File Implementation

We plan to realize our counter with one file (except the header file) in this section :

//Listing 1 The Counter
//c++
#include <stdio.h>
#include "counter.hxx"
 
	MyCounterImpl::MyCounterImpl()
		: m_nCount( 0 )
		{ printf( "< MyCounterImpl ctor called >\n" ); }
	MyCounterImpl::~MyCounterImpl()
		{ printf( "< MyCounterImpl dtor called >\n" ); }
	int MyCounterImpl::getCount() 
		{ return m_nCount; }
	void MyCounterImpl::setCount( int nCount ) 
		{ m_nCount = nCount; }
	int MyCounterImpl::increment() 
		{ return (++m_nCount); }
	int MyCounterImpl::decrement() 
		{ return (--m_nCount); }
 
int main(int argc, char **argv)
{
	MyCounterImpl Cnt;
	Cnt.setCount(50);
	printf("-- %d\n",Cnt.getCount());
	Cnt.increment();
	printf("-- %d\n",Cnt.getCount());
	Cnt.decrement();
	printf("-- %d\n",Cnt.getCount());
	return 0;
}

with the header file

//Listing 2 The header File of the Counter
// C++
// counter.hxx
class MyCounterImpl {
	int m_nCount;
public:
	MyCounterImpl();
	~MyCounterImpl();
	int getCount();
	void setCount( int nCount ); 
	int increment(); 
	int decrement() ;
};

What we want is to separate the code in two files : one for the class : counter.cxx and a second for the utilisation of the class countermain.cxx. The counter.cxx will be compiled in counter.so file and countermain will use this dynamic library.

Two Files Implementation

The corresponding way in C++ is again illustrated. First the dynamic library :

//Listing 3 Dynamic library
//c++
//counter.cxx
#include <stdio.h>
#include "counter.hxx"
 
MyCounterImpl::MyCounterImpl()
	: m_nCount( 0 )
	{ printf( "< MyCounterImpl ctor called >\n" ); }
MyCounterImpl::~MyCounterImpl()
	{ printf( "< MyCounterImpl dtor called >\n" ); }
int MyCounterImpl::getCount() 
	{ return m_nCount; }
void MyCounterImpl::setCount( int nCount ) 
	{ m_nCount = nCount; }
int MyCounterImpl::increment() 
	{ return (++m_nCount); }
int MyCounterImpl::decrement() 
	{ return (--m_nCount); }

This file is compiled with :

 gcc -shared -fPIC -o counter.uno.so counter.cxx

To use it I write this code :

//Listing 4 Main executable
//c++
//countermain.cxx
#include "counter.hxx"
#include <stdio.h>
int main(int argc, char **argv)
{
	MyCounterImpl Cnt;
	Cnt.setCount(50);
	printf("-- %d\n",Cnt.getCount());
	Cnt.increment();
	printf("-- %d\n",Cnt.getCount());
	Cnt.decrement();
	printf("-- %d\n",Cnt.getCount());
	return 0;
}

The compilation is then done with

 gcc -o test1 countermain.cxx -L ./ counter.uno.so -lstdc++

Running this example gives :

[smoutou@p3 component]$ ./test1
< MyCounterImpl ctor called >
-- 50
-- 51
-- 50
< MyCounterImpl dtor called >

This example is done without Openoffice.org. In the contrary the example given in the SDK make exacly the same but needs OpenOffice.org. Problems with shared library and C++ are examined here.

Making the Counter registerable

All dynamic libraries are not registrable. Taking the one constructed in previous section and give it to register, what's happening ?

[smoutou@p3 bin]$ regcomp -register -r counter.uno.rdb -c counter.uno.so
Aborted

Regcomp is completely unable to do correctly its job. Why ? If you want to be able to registry something you have to follow strict rules.

  • Rule 1

You have to construct first an IDL file. This IDL file allows you to construct an urd file and to add it in a rdb file. This has already been discussed above. See the figure below.

Registery.png

When examining this figure you see that a some.idl file is required (and naturally a uno.so file).

  • Rule 2

Your dynamic library have to contain extern C subprograms. Their names are :

// C++
extern "C" void SAL_CALL component_getImplementationEnvironment(
    sal_Char const ** ppEnvTypeName, uno_Environment ** ppEnv );
 
extern "C" sal_Bool SAL_CALL component_writeInfo(
    lang::XMultiServiceFactory * xMgr, registry::XRegistryKey * xRegistry );
 
extern "C" void * SAL_CALL component_getFactory(
    sal_Char const * implName, lang::XMultiServiceFactory * xMgr, void * );

First and second subroutines are for registry and third is for allowing UNO_QUERY calls. More information here where a fourth optional extern C subprogram is presented : getDescription.

  • Rule 3 (provisional rule)

Your component has to provide some other interfaces that those described in your IDL file : com.sun.star.uno.XInterface, com.sun.star.lang.XServiceInfo. The counter example provide these interfaces as shown in the listing below :

//Listing 5 The counter interfaces
// C++
class MyCounterImpl
	: public XCountable
	, public XServiceInfo
{
....

I suppose XCountable is an inheritance from XInterface. The figure above shows us a component in a schematic view.

ComponentInterface.png

The external rectangle in Figure above represents the component. In this component, we see a XCountable interface inheriting from XInterface and XServiceInfo and the three extern C callable functions.

The definitive rule 3 is presented later: please be patient or read the Developer's Guide on the subject.

The Counter Example (from SDK)

The Counter is provided with SDK here : <OpenOffice.org_SDK>/examples/cpp/counter. See a description of the SDK counter example.

Modifying the simple Counter to see registry at Work

Again, we start with the counter example included in the SDK at <OpenOffice.org1.1_SDK>/examples/cpp/counter. We modify it to see in the console how registry works and particularly the necessity of the rule 2 described in a previous section. The modification only consists of adding printf as shown in the listing below. The goal is to help us to see how it works.

//Listing 7 Modified Counter Component (extract)
// C++
.....
 
//*************************************************************************
OUString SAL_CALL MyCounterImpl::getImplementationName(  )
	throw(RuntimeException)
{
	printf("MyCounterImpl::getImplementationName(  ) called \n");
	return OUString( RTL_CONSTASCII_USTRINGPARAM(IMPLNAME) );
}
//*************************************************************************
sal_Bool SAL_CALL MyCounterImpl::supportsService( const OUString& ServiceName )
	throw(RuntimeException)
{
	printf("MyCounterImpl::supportsService called\n");
	Sequence< OUString > aSNL = getSupportedServiceNames();
	const OUString * pArray = aSNL.getArray();
	for( sal_Int32 i = 0; i < aSNL.getLength(); i++ )
		if( pArray[i] == ServiceName )
			return sal_True;
	return sal_False;
}	
 
//*************************************************************************
Sequence<OUString> SAL_CALL MyCounterImpl::getSupportedServiceNames(  )
	throw(RuntimeException)
{
	printf("MyCounterImpl::getSupportedServiceNames(  ) called \n");
	return getSupportedServiceNames_Static();
}
 
//*************************************************************************
Sequence<OUString> SAL_CALL MyCounterImpl::getSupportedServiceNames_Static(  )
{
	OUString aName( RTL_CONSTASCII_USTRINGPARAM(SERVICENAME) );
	printf("MyCounterImpl::getSupportedServiceNames_Static(  ) called with 
                                         %s\n",RTL_CONSTASCII_USTRINGPARAM(SERVICENAME));
	return Sequence< OUString >( &aName, 1 );
}
 
/**
 * Function to create a new component instance; is needed by factory helper implementation.
 * @param xMgr service manager to if the components needs other component instances
 */
Reference< XInterface > SAL_CALL MyCounterImpl_create(
	const Reference< XMultiServiceFactory > & xMgr )
{	printf("MyCounterImpl_create called\n");
	return Reference< XCountable >( new MyCounterImpl( xMgr ) );
}
 
//#####################################################################################
//#### EXPORTED ####################################################################################
//######################################################################################
/**
 * Gives the environment this component belongs to.
*/
extern "C" void SAL_CALL component_getImplementationEnvironment(const sal_Char ** ppEnvTypeName, 
                                                                  uno_Environment ** ppEnv) {
	printf("getImplementationEnvironnement return %s\n",CPPU_CURRENT_LANGUAGE_BINDING_NAME);
	*ppEnvTypeName = CPPU_CURRENT_LANGUAGE_BINDING_NAME;
}
 
/**
 * This function creates an implementation section in the registry and another subkey
 *
 * for each supported service.
 * @param pServiceManager   the service manager
 * @param pRegistryKey      the registry key
*/
extern "C" sal_Bool SAL_CALL component_writeInfo(void * pServiceManager, void * pRegistryKey)
{
	sal_Bool result = sal_False;
	printf("component_writeInfo called\n");
	if (pRegistryKey)
	{
	try
	{
		Reference< XRegistryKey > xNewKey(
		  reinterpret_cast< XRegistryKey * >( pRegistryKey )->createKey(
			OUString( RTL_CONSTASCII_USTRINGPARAM("/" IMPLNAME "/UNO/SERVICES") ) ) );
		printf("New key : %s\n",RTL_CONSTASCII_USTRINGPARAM("/" IMPLNAME "/UNO/SERVICES"));
		printf("--component_writeInfo calls MyCounterImpl::getSupportedServiceNames_Static()\n");
		const Sequence< OUString > & rSNL =
			MyCounterImpl::getSupportedServiceNames_Static();
		const OUString * pArray = rSNL.getConstArray();
		for ( sal_Int32 nPos = rSNL.getLength(); nPos--; ) {
			xNewKey->createKey( pArray[nPos] );
			printf("----Sub-Key : %s build\n",OUStringToOString(pArray[nPos],
									RTL_TEXTENCODING_ASCII_US)
			.pData->buffer);
		}
		return sal_True;
	}
	catch (InvalidRegistryException &)
	{
		// we should not ignore exceptions
	}
	}
	return result;
}
 
/**
 * This function is called to get service factories for an implementation.
 *
 * @param pImplName       name of implementation
 * @param pServiceManager a service manager, need for component creation
 * @param pRegistryKey    the registry key for this component, need for persistent data
 * @return a component factory
 */
/**/
extern "C" void * SAL_CALL component_getFactory(const sal_Char * pImplName, 
                                              void * pServiceManager, void * pRegistryKey){
	void * pRet = 0;
	printf("component_getFactory called\n");
	if (rtl_str_compare( pImplName, IMPLNAME ) == 0)
	{
		Reference< XSingleServiceFactory > xFactory( createSingleFactory(
			reinterpret_cast< XMultiServiceFactory * >( pServiceManager ),
			OUString( RTL_CONSTASCII_USTRINGPARAM(IMPLNAME) ),
			MyCounterImpl_create,
			MyCounterImpl::getSupportedServiceNames_Static() ) );
 
		if (xFactory.is())
		{
			xFactory->acquire();
			pRet = xFactory.get();
		}
	}
	return pRet;
}

With countermain slightly modified to know where the registry begins, this piece of code produces the following output :

[smoutou@p3 counter]$ make countermain.run
cd ../../../LINUXexample.out/bin && countermain
Here begin registry
getImplementationEnvironnement return gcc3
component_writeInfo called
New key : /com.sun.star.comp.example.cpp.Counter/UNO/SERVICES
--component_writeInfo calls MyCounterImpl::getSupportedServiceNames_Static()
MyCounterImpl::getSupportedServiceNames_Static(  ) called with foo.Counter
----Sub-Key : foo.Counter build
Here ends registry and begin instanciation
getImplementationEnvironnement return gcc3
component_getFactory called
MyCounterImpl::getSupportedServiceNames_Static(  ) called with foo.Counter
MyCounterImpl_create called
< MyCounterImpl2 ctor called >
42,43,42
Another registry use : getImplementations
-- com.sun.star.comp.bridge.UnoUrlResolver
< MyCounterImpl2 dtor called >
[smoutou@p3 counter]$

while a direct regcomp call produces this following output :

[smoutou@p3 bin]$ regcomp -register -r counter.uno.rdb -c counter.uno.so
getImplementationEnvironnement return gcc3
component_writeInfo called
New key : /com.sun.star.comp.example.cpp.Counter/UNO/SERVICES
--component_writeInfo calls MyCounterImpl::getSupportedServiceNames_Static()
MyCounterImpl::getSupportedServiceNames_Static(  ) called with foo.Counter
----Sub-Key : foo.Counter build
register component 'counter.uno.so' in registry 'counter.uno.rdb' succesful!
[smoutou@p3 bin]$

We can draw from both shell ouputs that registry works the same way, in the both case as expected. The first call is asking for Environment which return “gcc3” here, and after componentwriteinfo which is responsible of registry itself. A question : how do we see our counter with OOoBasic at this step ? Because our counter is registered we see it (partially) but we cannot use it. (Note you have to stop countermain with a getchar to have time launch a OOoBasic program.)

'Listing 8  Little OOoBasic Program to see our Counter.
 
REM  *****  BASIC  *****
 
Sub Main
	ocmpt = createUnoService("foo.Counter")
	XRay oCmpt
End Sub

Remark about OpenOffice build framework

When working in the OpenOffice build framework, make sure to add these functions to the util/NAME.map file, eg when adding the first UNO service to calc's scfilt library, do

--- sc/util/scfilt.map
+++ sc/util/scfilt.map
@@ -1,6 +1,9 @@
 SCFILT_1_0 {
   global:
     ScFilterCreate;
+    component_getImplementationEnvironment;
+    component_writeInfo;
+    component_getFactory;
   local:
     *;
 };

and that you tell scp2 the library is a UNO library, in the case of scfilt, do

--- scp2/source/calc/file_calc.scp
+++ scp2/source/calc/file_calc.scp
@@ -49,7 +49,7 @@ STD_UNO_LIB_FILE_PATCH( gid_File_Lib_Sc, sc)
 
 STD_LIB_FILE_PATCH( gid_File_Lib_Scui, scui)
 
-STD_LIB_FILE( gid_File_Lib_Scfilt, scfilt)
+STD_UNO_LIB_FILE( gid_File_Lib_Scfilt, scfilt)
 
 STD_UNO_LIB_FILE( gid_File_Lib_Scd, scd)

Results

XRay is a OOoBasic tool very easy to use for introspection. Bernard Marcelly's XRay tool description can be found in this Wiki or you can download it here

XRaying this object as shown in the above listing, gives us :

properties

--- Object internal name : ( no name )
Dbg_Methods 
        string <...> basic prop, read-only
Dbg_Properties            
        string <...> basic prop, read-only
Dbg_SupportedInterfaces
        string <...> basic prop, read-only

methods

--- Object internal name : ( no name )
queryInterface  ( aType as type )  AS variant
acquire         (  )
release         (  )

supported interfaces

--- List of supported interfaces ---
com.sun.star.uno.XInterface

As shown above, only the XInterface can bee seen : not great ! We can therefore instantiate it but not call one of its own method. The next step is to use the counter with OOoBasic : in other words, to make it scriptable.

Using an Helper to construct the scriptable Counter

Starting from the Counter example we hope to modify the counter class and make it scriptable.

If you want to make your component scriptable you have to modify the previous rule 3 as this new one :

  • Rule 3

Your component has to provide some interfaces : com.sun.star.uno.XInterface, com.sun.star.lang.XServiceInfo, com.sun.star.uno.XWeak, and com.sun.star.lang.XTypeProvider. The last one is then a characteristic of scriptable component. See also the Developer's Guide.

ScripComponent.png

The Figure above shows us a scriptable component with a schematic view (Yes I have to change this figure because of one interface lack. As we will show later in fact we are using an helper and this means we haven't to implement all Interfaces : only com.sun.star.lang.XServiceInfo is needed : others are implemented automatically with the helper as mentionned in the Developer's Guide.

What about our makefile ? We are no more interested in calling this counter with countermain.cxx but using it in OOoBasic that means we have to register our dynamic library. Two solutions :

  1. we let again countermain to register the counter and we stop it after registered and then try to launch OOoBasic program,
  2. we look for an example which correctly registers. One is provided with the SDK as mentioned above :<OOo_SDK>/examples/DevelopersGuide/Components/CppComponent

and we can use the makefile of this component.

Documentation caution.png For a try I use the first solution but never make it working. There was probably a problem with registery. If a second program manage registry as in the counter example, I am unable to see the component correctly.

It works when using the makefile of CppComponent renaming the IDL file and cxx file in Makefile as mentioned below.

To do : to go further with the registery problem !!!

To explain how to construct our component I will start from the SDK example in <OOo_SDK>/examples/DevelopersGuide/Components/CppComponent directory. First copy this directory in <OOo_SDK>/examples/DevelopersGuide/Components/MyComponent for instance. The point is to keep a subdirectory of <OOo_SDK>/examples/DevelopersGuide/Components to minimize the change in makefile but every name can be used.

CppComponent.uno.xml File

In your new <OOo_SDK>/examples/DevelopersGuide/Component/MyComponent directory, you will find a file CppComponent.uno.xml. Open it and replace

 <type>my_module.XSomething</type>
 <type>my_module.MyService1</type>
 <type>my_module.MyService2</type>

by

 <type>foo.XCountable</type>
 <type>foo.Counter</type>

Makefile File

In the original Makefile, replace

 
  IDLFILES = some.idl

by

  IDLFILES = Counter.idl

and replace also

  CXXFILES = service1_impl.cxx \
	service2_impl.cxx

by

  CXXFILES = service_impl.Last.OK.cxx 
  #	service2_impl.cxx

where you adapt your own source file name.

IDL file

You have no change to do in the Counter example.

Counter C++ Source File

To access the counter interface in Basic, you require the same IDL names as in Listing 6 ; we give now the complete C++ counter program.

//Listing 9 The complete Counter Program
// C++
// counter.cxx
/***************************************************************************************************
 ***************************************************************************************************
 *
 * service implementation:	 foo.Counter
 * exported interfaces:		 foo.XCounter
 *
 * simple example component implementing a counter
 *
 ***************************************************************************************************
 **************************************************************************************************/
 
namespace my_sc_impl
{
static Sequence< OUString > getSupportedServiceNames_MyCounterImpl()
{
	static Sequence < OUString > *pNames = 0;
	if( ! pNames )
	{
//		MutexGuard guard( Mutex::getGlobalMutex() );
		if( !pNames )
		{
			static Sequence< OUString > seqNames(1);
			seqNames.getArray()[0] = OUString(RTL_CONSTASCII_USTRINGPARAM(SERVICENAME));
			pNames = &seqNames;
		}
	}
	return *pNames;
}
 
static OUString getImplementationName_MyCounterImpl()
{
	static OUString *pImplName = 0;
	if( ! pImplName )
	{
//		MutexGuard guard( Mutex::getGlobalMutex() );
		if( ! pImplName )
		{
			static OUString implName( RTL_CONSTASCII_USTRINGPARAM(IMPLNAME) );
			pImplName = &implName;
		}
	}
	return *pImplName;
}
 
// New
class MyCounterImpl : public ::cppu::WeakImplHelper2<
      XCountable, XServiceInfo >
{
	// to obtain other services if needed
	Reference< XMultiServiceFactory > m_xServiceManager;
 
	sal_Int32 m_nRefCount;
	sal_Int32 m_nCount;
 
public:
    // XServiceInfo	specification
    virtual OUString SAL_CALL getImplementationName(  ) throw(RuntimeException);
    virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) throw(RuntimeException);
    virtual Sequence< OUString > SAL_CALL getSupportedServiceNames(  ) throw(RuntimeException);
    static Sequence< OUString > SAL_CALL getSupportedServiceNames_Static(  );
 
	// XCountable implementation
	virtual sal_Int32 SAL_CALL getCount() throw (RuntimeException)
		{ return m_nCount; }
	virtual void SAL_CALL setCount( sal_Int32 nCount ) throw (RuntimeException)
		{ m_nCount = nCount; }
	virtual sal_Int32 SAL_CALL increment() throw (RuntimeException)
		{ return (++m_nCount); }
	virtual sal_Int32 SAL_CALL decrement() throw (RuntimeException)
		{ return (--m_nCount); }
};
// XServiceInfo	implementation
//*************************************************************************
OUString SAL_CALL MyCounterImpl::getImplementationName(  )
	throw(RuntimeException)
{
	printf("MyCounterImpl::getImplementationName(  ) called \n");
	return OUString( RTL_CONSTASCII_USTRINGPARAM(IMPLNAME) );
}
 
//*************************************************************************
sal_Bool SAL_CALL MyCounterImpl::supportsService( const OUString& ServiceName )
	throw(RuntimeException)
{
	printf("MyCounterImpl::supportsService called\n");
	Sequence< OUString > aSNL = getSupportedServiceNames();
	const OUString * pArray = aSNL.getArray();
	for( sal_Int32 i = 0; i < aSNL.getLength(); i++ )
		if( pArray[i] == ServiceName )
			return sal_True;
	return sal_False;
}	
 
//*************************************************************************
Sequence<OUString> SAL_CALL MyCounterImpl::getSupportedServiceNames(  )
	throw(RuntimeException)
{
	printf("MyCounterImpl::getSupportedServiceNames(  ) called \n");
	return getSupportedServiceNames_Static();
}
 
//*************************************************************************
Sequence<OUString> SAL_CALL MyCounterImpl::getSupportedServiceNames_Static(  )
{
	OUString aName( RTL_CONSTASCII_USTRINGPARAM(SERVICENAME) );
	printf("MyCounterImpl::getSupportedServiceNames_Static(  ) called with %s\n",RTL_CONSTASCII_USTRINGPARAM(SERVICENAME));
	return Sequence< OUString >( &aName, 1 );
}
 
//***********NEW
Reference< XInterface > SAL_CALL MyCounterImpl_create(
    Reference< XComponentContext > const & xContext )
    SAL_THROW( () )
{
    return static_cast< XTypeProvider * >( new MyCounterImpl() );
}
 
}
//##################################################################################################
//#### EXPORTED ####################################################################################
 
/* shared lib exports implemented without helpers in service_impl1.cxx */
namespace my_sc_impl
{
static struct ::cppu::ImplementationEntry s_component_entries [] =
{
    {
        MyCounterImpl_create, getImplementationName_MyCounterImpl,
        getSupportedServiceNames_MyCounterImpl, ::cppu::createSingleComponentFactory,
        0, 0
    },
    { 0, 0, 0, 0, 0, 0 }
};
}
 
extern "C"
{
void SAL_CALL component_getImplementationEnvironment(
    sal_Char const ** ppEnvTypeName, uno_Environment ** ppEnv )
{
    *ppEnvTypeName = CPPU_CURRENT_LANGUAGE_BINDING_NAME;
}
sal_Bool SAL_CALL component_writeInfo(
    XMultiServiceFactory * xMgr, XRegistryKey * xRegistry )
{
    return ::cppu::component_writeInfoHelper(
        xMgr, xRegistry, ::my_sc_impl::s_component_entries );
}
void * SAL_CALL component_getFactory(
    sal_Char const * implName, XMultiServiceFactory * xMgr,
    XRegistryKey * xRegistry )
{
    return ::cppu::component_getFactoryHelper(
        implName, xMgr, xRegistry, ::my_sc_impl::s_component_entries );
}
}

The Makefile is the same as in the previous example. Template:Documentation/Note

TestCppComponent.cxx File

In your directory you have a file named TestCppComponent.cxx which doesn't work anymore because written for an other service. You can set all the "main()" content in comment. Then your counter test could not be done typping a command :

make TestCppComponent.run

but only with command

make SimpleComponent.odt.load

and you have to modify the OOoBasic program of SimpleComponent.odt document as mentioned below. We will learn how to modify TestCppComponent.cxx later.

OOoBasic program for a test

The OOoBasic program to test this example is as follows:

'Listing 10 OOoBasic test Program
REM  *****  BASIC  *****
Sub Main
	Dim oSimpleComponent
	oSimpleComponent = CreateUnoService( "foo.Counter" )
	oSimpleComponent.setCount(5)
	print oSimpleComponent.getCount()
	oSimpleComponent.increment()
	print oSimpleComponent.getCount()
	XRay oSimpleComponent
End Sub

Inspecting the new created service with XRay shows :

properties

---- Object internal name : com.sun.star.comp.example.cpp.Counter
Count                     long          
ImplementationName        string 
SupportedServiceNames  ]  string               
Types                     []type                 
ImplementationId          []byte                 
Dbg_Methods               string       
Dbg_Properties            string       
Dbg_SupportedInterfaces   string       

methods

--- Object internal name : com.sun.star.comp.example.cpp.Counter
queryInterface            ( aType as type )                
acquire                   (  ) 
release                   (  ) 
getCount                  (  )                             
setCount                  ( nCount as long ) 
increment                 (  )                             
decrement                 (  )                             
getImplementationName     (  )                             
supportsService           ( ServiceName as string )        
getSupportedServiceNames  (  )                             
getTypes                  (  )                             
getImplementationId       (  )                             
queryAdapter              (  )

You see now the counter methods in OOoBasic (XRay is a OOoBasic tool)

Template:Documentation/Note The use of Java inspector could be done with the OOoBasic program :

'Listing 10b Programme OOoBasic de test
REM  *****  BASIC  *****
Dim oSimpleComponent
	oSimpleComponent = CreateUnoService( "foo.Counter" )
	oInspector = createUnoService("org.openoffice.InstanceInspector")
	oInspector.inspect(oSimpleComponent, "MyCounter")
End Sub

Template:Documentation/Note

// C++ Daniel Bölzle
....
class MyCounterImpl : public cppu::WeakImplHelper1< XCountable >
{
	// to obtain other services if needed
	Reference< XMultiServiceFactory > _xServiceManager;
 
	sal_Int32 _nCount;
 
	Reference< XText > _xText;
	void dump( sal_Int32 nValue ) const;
 
public:
	MyCounterImpl( const Reference< XMultiServiceFactory > & xServiceManager );
	virtual ~MyCounterImpl();
 
	// XCountable implementation
	virtual sal_Int32 SAL_CALL getCount() throw (RuntimeException);
	virtual void SAL_CALL setCount( sal_Int32 nCount ) throw (RuntimeException);
	virtual sal_Int32 SAL_CALL increment() throw (RuntimeException);
	virtual sal_Int32 SAL_CALL decrement() throw (RuntimeException);
};
...

First Variation : a Counter with Attribute

A little variation on the counter component. I decide to use the attribute possibility in an IDL file. Bear in mind an attribute is automatically accessed with set/get methods. These methods are automatically created. We have then to remove the corresponding methods from the IDL file. My Counter IDL file becomes then :

//Listing 11 Our new IDL File
//IDL
#include <com/sun/star/uno/XInterface.idl>
//#include <com/sun/star/lang/XInitialization.idl>
 
module foo
{
 
interface XCountable : com::sun::star::uno::XInterface
{
	// long getCount(); ************* generee automatiquement
	// void setCount( [in] long nCount ); ********** generee automatiquement
    [attribute] long Count;
	long increment();
	long decrement();
};
 
service Counter
{
    interface XCountable;
};
};

How is this new counter seen with OooBasic ? We don't see getCount and setCount anymore ! That doesn't mean they don't exist anymore. To prove their existence we program this OooBasic program and run it successfully.

'Listing 12 
REM  *****  BASIC  *****
 
Sub Main
	Dim oSimpleComponent
	oSimpleComponent = CreateUnoService( "foo.counter" )
	oSimpleComponent.Count = 5
	print oSimpleComponent.Count
	oSimpleComponent.increment()
	print oSimpleComponent.Count
	XRay.XRay oSimpleComponent
End Sub

XRaying this object gives us : properties

--- Object internal name : com.sun.star.comp.example.cpp.Counter
Count                     long                    
ImplementationName        string       <...>     pseudo-prop, read-only 
SupportedServiceNames     []string               pseudo-prop, read-only 
Types                     []type                 pseudo-prop, read-only 
ImplementationId          []byte                 pseudo-prop, read-only 
Dbg_Methods               string       <...>     basic prop, read-only
Dbg_Properties            string       <...>     basic prop, read-only
Dbg_SupportedInterfaces   string       <...>     basic prop, read-only  

methods

--- Object internal name : com.sun.star.comp.example.cpp.Counter
queryInterface            ( aType as type )                AS variant 
acquire                   (  ) 
release                   (  ) 
increment                 (  )                             AS long 
decrement                 (  )                             AS long 
getImplementationName     (  )                             AS string 
supportsService           ( ServiceName as string )        AS boolean 
getSupportedServiceNames  (  )                             AS []string 
getTypes                  (  )                             AS []type 
getImplementationId       (  )                             AS []byte 
queryAdapter              (  )                             AS object 

Template:Documentation/Note

Second Variation : using the Service Manager in the Counter

You can find a tutorial describing a scriptable counter example (from Daniel Bölzle) here where the counter is opening a OOoCalc document, that means it uses OOo services and interfaces.

The Service Manager management in counter component has been postponed later here.

Constructing the scriptable Counter without Helper

We have seen how using helper avoid any interfaces coding. We want construct the counter but without helper, that means we will write everything we need. At the moment see the Developper's Guide.

TODO: There's more to come here ...

Going further with components and dialog

Developer's Guide states it is possible to construct a component with a dialog that have been created with the Dialog Editor integrated in the OpenOffice.org Basic IDE.

I have worked around this problem in a new chapter here. Template:Documentation/Tip

See also Generic UNO Interfaces for complex toolbar controls.

HomePageCpp.png Return to Document Home page

See also

Personal tools