Difference between revisions of "Constructing Components"
SergeMoutou (Talk | contribs) m (→Constructing the scriptable Counter without Helper) |
SergeMoutou (Talk | contribs) m (→TO DO : going further with components and dialog) |
||
Line 894: | Line 894: | ||
}} | }} | ||
− | == | + | == Going further with components and dialog == |
+ | [[Documentation/DevGuide/WritingUNO/Accessing_Dialogs | 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 [[Component_and_Dialog|in a new chapter here]]. | ||
{{Documentation/Tip|It is probably possible to create dialog at run time in a component. Installing Event listener for buttons is probably possible with [[Uno/Cpp/Tutorials/Introduction_to_Cpp_Uno#The_Component_helper | the Component helper]].}} | {{Documentation/Tip|It is probably possible to create dialog at run time in a component. Installing Event listener for buttons is probably possible with [[Uno/Cpp/Tutorials/Introduction_to_Cpp_Uno#The_Component_helper | the Component helper]].}} | ||
− | |||
− | |||
− | |||
− | |||
See also [[Framework/Article/Generic_UNO_Interfaces_for_complex_toolbar_controls|Generic UNO Interfaces for complex toolbar controls]]. | See also [[Framework/Article/Generic_UNO_Interfaces_for_complex_toolbar_controls|Generic UNO Interfaces for complex toolbar controls]]. | ||
− | |||
− | |||
− | |||
− | |||
[[Image:HomePageCpp.png]] [[Using_Cpp_with_the_OOo_SDK|Return to Document Home page]] | [[Image:HomePageCpp.png]] [[Using_Cpp_with_the_OOo_SDK|Return to Document Home page]] |
Revision as of 15:17, 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.
Contents
- 1 Danny Brewer's Terminology
- 2 Compilation Chain of a Component
- 3 First Example : accessing a Counter from OOoBasic
- 3.1 A very simple Counter
- 3.2 Making the Counter registerable
- 3.3 The Counter Example (from SDK)
- 3.4 Using an Helper to construct the scriptable Counter
- 3.5 First Variation : a Counter with Attribute
- 3.6 Second Variation : using the Service Manager in the Counter
- 3.7 Constructing the scriptable Counter without Helper
- 3.8 Going further with components and dialog
- 4 See also
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.
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 :
- Create the idl and cpp files. Figure above shows these files as some.idl and some.cpp.
- Use idlc to compile the idl file to a urd file.
- 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.
- Use cppumaker to create the hpp and hdl header files.
- Use gcc to compile the cpp file.
- The final registry step has been discussed previously and is not shown in Figure above.
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.
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.
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.
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 :
- we let again countermain to register itself and we stop it after registered and then try the OOoBasic program,
- 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.
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
// 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
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.
See also
- Daniel Bölzle's tutorial : Writing a simple UNO component
- Old Service Manager and new Context passing when instanciating a component (see Developer's Guide)
- Writing UNO Components in Developer's Guide
- Generic UNO Interfaces for complex toolbar controls
- Helper Not Helper
- C++ and UNO tutorial
- Creating an URE application in C++
- Service Declaration
- Registering properties
- UNO tutorial
- UNO IDL
- Extensions Packager (BasicAddonBuilder from Paolo Mantovani)
- BASIC UNO Object Browser : You can see the corresponding code as a complex component.
- Minimal Java Component
- General UNO Component project Type in Java
- Office Add-on Projet Type in Java
- Uno/Article/Types&Reflection
- Component with Python
- Writing a Program to Control OpenOffice.org, by Franco Pingiori — Part 1 and Part 2, Linux Journal
- Bernard Marcelly's XRay OOoBasic tool and a XRay tool description in this wiki
- See also Extension Deployement
- (Python) UNO component packaging