UNO-ificandoBibliotecaNueva
From OpenOffice.org Wiki
[ El bloque anterior de tutoriales de 'helloworld' mostraron como construir una biblioteca e invocarla desde un proyecto diferente. Este tutorial continua desde ese punto. ]
Asi que continuamos desde donde usamos nuestra nueva biblioteca con el digalogo de svx/ charmap. Aqui introdujimos un nuevo proyecto, pero ahora para hacerlo mas interesante lo haremos que trabaje de la forma donde la mayor parte del codigo funciona -- como un componente de UNO!
Los componentes de UNO los llamavos comunmente 'servicios', y implementacion de servicios de interfaz. Escogemos la interfaz mas pequeña con la cual podemos trabajar, con - XExecutableDialog, con sus dos metodos especificados: execute() y setTitle() y decido que esto es la interfaz que sera implementado como nuestro servicio de helloworld. Lo primero sera modificar el encabezado del archivo:
--- helloworld/inc/helloworld.hxx 2005-07-20 13:17:57.504281011 +0530
+++ helloworld/inc/helloworld.hxx 2005-07-20 13:26:05.653411957 +0530
+#ifndef _COM_SUN_STAR_UI_DIALOGS_XEXECUTABLEDIALOG_HPP_
+#include <com/sun/star/ui/dialogs/XExecutableDialog.hpp>
+#endif
namespace hello {
+
namespace world {
+
- class HelloWorld {
+ class HelloWorld : public WeakImplHelper1< XExecutableDialog > {
+
+ Reference< XMultiServiceFactory > _xServiceManager;
+
public:
+
+ virtual void SAL_CALL setTitle( const OUString& aTitle )
+ throw( RuntimeException );
+
+ virtual sal_Int16 SAL_CALL execute( )
+ throw( RuntimeException );
+
+ HelloWorld( const Reference< XMultiServiceFactory > & xServiceManager );
+
void adios();
+
};
Cada interfaz tiene un encabezado que se incluye primero, y despues usa la ayuda del WeakImpHelper1 para manejar nuestros servicios mejor. Then we specify the methods in the interface that we implement - every method in the interface needs to be implemented since they are all virtual. The ServiceManager reference is passed in to all components, which we also retain.
Then we flesh out the body of the methods in the component:
--- helloworld/source/helloworld.cxx 2005-07-20 13:18:55.965713320 +0530
+++ helloworld/source/helloworld.cxx 2005-07-20 13:33:52.756043677 +0530
+// XExecutableDialog Methods
+void SAL_CALL HelloWorld::setTitle( const OUString& rTitle ) throw( RuntimeException )
+{
+ fprintf( stderr, "HelloWorld::setTitle: %s\n", OU2A( rTitle ) );
+}
+
+sal_Int16 SAL_CALL HelloWorld::execute() throw( RuntimeException )
+{
+ fprintf( stderr, "HelloWorld::execute\n" );
+}
The real idea of the interface is really to launch a dialog and operate it till it is shutdown. We'll just output a simple string just to keep the patch short :-)
[ While we're here, since we hit an OUString that is used throughout OOo, let's just drop in the OU2A macro: +#define OU2A(rtlOUString) (::rtl::OUStringToOString((rtlOUString), RTL_TEXTENCODING_ASCII_US).getStr())
since that comes in handy a lot of the time :-)]
Then we need to implement a function that takes in the ServiceManager as a parameter and instantiates our object that implements the service:
--- helloworld/source/helloworld.cxx 2005-07-20 13:18:55.965713320 +0530
+++ helloworld/source/helloworld.cxx 2005-07-20 13:33:52.756043677 +0530
+// UNO component instantiator class
+Reference< XInterface > createHelloWorld(
+ const Reference< XMultiServiceFactory > & xMgr )
+{
+ return Reference< XInterface >( static_cast< XExecutableDialog* >( new HelloWorld( xMgr ) ) );
+}
And in order to keep track of the ServiceManager, we include a corresponding constructor for our class:
--- helloworld/source/helloworld.cxx 2005-07-20 13:18:55.965713320 +0530
+++ helloworld/source/helloworld.cxx 2005-07-20 13:33:52.756043677 +0530
+HelloWorld::HelloWorld( const Reference< XMultiServiceFactory > & xServiceManager )
+ : _xServiceManager( xServiceManager )
+{
+}
That takes care of the operation of the component entirely. Now to fill in the pieces the UNO environment needs for operation:
--- helloworld/source/helloworld.cxx 2005-07-20 13:18:55.965713320 +0530
+++ helloworld/source/helloworld.cxx 2005-07-20 13:33:52.756043677 +0530
+void SAL_CALL component_getImplementationEnvironment(
+ const sal_Char ** ppEnvTypeName, uno_Environment ** ppEnv )
+{
+ *ppEnvTypeName = CPPU_CURRENT_LANGUAGE_BINDING_NAME;
+}
This function specifies the environment of the component, which in this case specifies the C++ component environment as opposed to the others.
--- helloworld/source/helloworld.cxx 2005-07-20 13:18:55.965713320 +0530
+++ helloworld/source/helloworld.cxx 2005-07-20 13:33:52.756043677 +0530
+sal_Bool SAL_CALL component_writeInfo( void* pServiceManager, void* pRegistryKey )
+{
+ if ( pRegistryKey )
+ {
+ Reference< XRegistryKey > xNewKey( reinterpret_cast< XRegistryKey * >( pRegistryKey )->createKey(
+ OUString( RTL_CONSTASCII_USTRINGPARAM("/org.openoffice.world.hello/UNO/SERVICES") ) ) );
+ xNewKey->createKey( OUString( RTL_CONSTASCII_USTRINGPARAM("org.openoffice.helloWorld") ) );
+ return sal_True;
+ }
+ return sal_False;
+}
This portion is used to register the component - to write the information about the component into a registry. It says that the name of the service is "org.openoffice.helloWorld" and the service implementation has the name "org.openoffice.world.hello". Because of this what happens is that when someone queries for the service name, first the name of the implementation is given to them for further action.
--- helloworld/source/helloworld.cxx 2005-07-20 13:18:55.965713320 +0530
+++ helloworld/source/helloworld.cxx 2005-07-20 13:33:52.756043677 +0530
+void * SAL_CALL component_getFactory(
+ const sal_Char * pImplName, void * pServiceManager, void * pRegistryKey )
+{
+ void * pRet = 0;
+ if (pServiceManager && !rtl_str_compare( pImplName, "org.openoffice.world.hello" ))
+ {
+ OUString aServiceName( RTL_CONSTASCII_USTRINGPARAM("org.openoffice.helloWorld ") );
+ Reference< XSingleServiceFactory > xFactory(
+ createSingleFactory(
+ reinterpret_cast< XMultiServiceFactory * >( pServiceManager ),
+ OUString::createFromAscii( pImplName ),
+ createHelloWorld,
+ Sequence< OUString >( &aServiceName, 1 ) ) );
+ if (xFactory.is())
+ {
+ xFactory->acquire();
+ pRet = xFactory.get();
+ }
+ }
+ return pRet;
+}
This function is what is executed when someone queries for a factory that can create an instance of the component that contains the implementation that the registry specifies. What it says is that if someone queries for the implementation our component provides - "org.openoffice.world.hello", then return a factory to them with the name of the instantiating function which here is 'createHelloWorld'.
These 3 functions are enclosed in an extern "C" so that the binary interface is exported without C++ mangling of the function names, and our component is ready to be built with one more change:
--- helloworld/source/makefile.mk 2005-07-20 13:15:10.908549636 +0530 +++ helloworld/source/makefile.mk 2005-07-09 17:20:38.000000000 +0530 @@ -15,6 +15,8 @@ SLOFILES=\ + SHL1TARGET= hworld$(UPD)$(DLLPOSTFIX) SHL1LIBS= $(SLB)$/helloworld.lib +SHL1STDLIBS=\ + $(CPPUHELPERLIB) + # --- Targets ----------------------------------
Voila! That builds our shining new UNO helloworld component for us, all ready and waiting for action, so we shift gears in the charmap dialog and invoke our method UNO style.

