UNO入门(转)

 

你知道UNO吗?

--------------------------------------------------------------------------------

[上几讲我们介绍了新的顶层工程'helloworld',及他如何编译成库,如何被不同的顶层工程调用,
这节我们从这点继续往前走。]

我们会离开svx下的charmap使用新库,
我们会介绍一个新的工程,但是是更感兴趣的,最多的OOo代码组织方式-作为一个UNO组件。

UNO组件通常可以称作服务(services),服务实现接口'interface'. 所以我们选择我们可以工作的最小接口
-XExecutableDialog, 这个接口有两个方法:execute()和setTitle(), 实现这两个接口就可以实现我们的
helloworld服务, 第一步是修改头文件:


--- 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
+#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();
 
                };

每一个接口首先有一个我们需要包含的头文件,然后我们需要使用WeakImplHelper1来帮助更好的管理我们的服务。
我们指定接口所实现的方法。接口的每一个方法都需要实现,因为他们都是虚函数。我们需要保留的服务管理器引
用也传给所有的组件。

然后我们实现组件中的函数内容:

--- 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" );
+}

这个接口真实的主意是启动一个对话框,操作它直到他关闭。我们为了保持这个补丁足够小,就仅仅是输出一个简单的字符串。


[ 在这里,我们遇到一个在整个OOo中都会用到的OUString, 让我们定义一个OU2A宏。:

+#define OU2A(rtlOUString)  (::rtl::OUStringToOString((rtlOUString), RTL_TEXTENCODING_ASCII_US).getStr())
这样我们就可以节省很多时间 :-)]

我们需要实现一个函数使用ServiceManager作为参数来实例化实现这个服务的对象:

--- 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 ) ) );
+}
为了跟踪ServiceManager,我们需要一个相应的构造函数:

--- 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 )
+{
+}

为了实现整个组件的操作,现在填充UNO环境操作需要的东西:
--- 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;
+}
这个函数指定了组件的环境,这里指定了C++组件环境,相对的还有其他组件环境。


--- 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;
+}

这部分用于注册组件--写这个组件的信息到注册表。这是说服务名是"org.openoffice.helloWorld",服务的实现是"
org.openoffice.world.hello". 由于这些在查询服务时发生,首先名字要给出他进一步行动的提示。

--- 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;
+}

这个函数的功能是当查询包含可以创建注册表中的实现时的工厂,这个工厂可以创建组件的实例。也就是说,某人查询我们
组件的实现"org.openoffice.world.hello", 就返回初始化功能的工厂,这里是'createHelloWorld'.

有三个函数用extern "C"包围,所以二进制接口没有用C++转换导出函数名。我们的组件需要再做一出更改就可以编译了:

--- 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 ----------------------------------
 
编译我们新的UNO helloworld组件已经好了,就等待下一步动作了,所以我们切换到charmap对话框,调用我们UNO风格的方法。

 

 

 

Tutorial UNO Client
From OpenOffice.org Wiki

现在我们的组件已经编译好了,可以在其他地方调用了,我们还是在以前同一个地方使用这个动态库:

--- svx/source/dialog/charmap.cxx       2004-07-13 15:15:11.000000000 +0530
+++ svx/source/dialog/charmap.cxx       2005-07-20 13:57:39.557992653 +0530
@@ -117,6 +119,9 @@
 #ifndef _COMPHELPER_TYPES_HXX_
 #include <comphelper/types.hxx>
 #endif
+#ifndef _UNOTOOLS_PROCESSFACTORY_HXX
+#include <comphelper/processfactory.hxx>
+#endif
 
 #include "rtl/ustrbuf.hxx"

@@ -1097,6 +1102,15 @@ void SvxCharMapData::SetCharFont( const
 
 IMPL_LINK( SvxCharMapData, OKHdl, OKButton *, EMPTYARG )
 {
+    Reference< XExecutableDialog > xHelloWorld( ::comphelper::getProcessServiceFactory()->
+        createInstance( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("org.openoffice.helloWorld") ) ), UNO_QUERY );
+
+    if( xHelloWorld.is() )
+        xHelloWorld->execute();
+    else
+        fprintf( stderr, "Unable to instantiate xHelloWorld.\n" );
+
+
     String aStr = aShowText.GetText();
 
     if ( !aStr.Len() )

通过调用全局服务工厂来创建新组件实例,我们传递想要使用的新组件的名字,请求他来创建组件的实例。

失败了,:-) 所以发生了打印出错信息说,不能初始化xHelloWorld

当组件编译准备好时,所有的组件被注册到中心注册表中,这个注册表详细说明了可利用的全部组件,和他们的实现在那里。注册表在$OOoInstall/program/services.rdb. ServiceManager将在那里寻找我们的服务。将会返回空,因为它没有主意到我们的组件,因此查询我们的服务失败。

输入regview和regcomp  :-) regview是注册表查看器,在registry/目录中被编译,拷贝到了solver目录,所以regview的路径是:solver/680 /unxlngi4.pro/bin/regview. 如果 linuxIntelEnvSet.sh的环境变量被设置了,就直接可以找到,因为solver的bin目录在$PATH环境变量中。

我们切换到$OOoInstall/program目录中,输入:

regview services.rdb | less

我们可以看到服务的整个列表和他们的实现,检查是否存在'helloWorld',确认了这个库没有被注册到里面。

因此需要注册我们所实现的组件,输入如下:

regcomp -register -r services.rdb -c libhworld680li.so

[ 这假定已经拷贝我们的库到安装目录下了。或者在安装集里已经添加了这个库。]

通过regview来检查是否已经注册成功! :-) 输出应该和下面类似:

Registry "file:///home/raul/opt/m110/program/services.rdb":

/
 / IMPLEMENTATIONS
...
...
...
   / org.openoffice.world.hello
     / UNO
       / ACTIVATOR
         Value: Type = RG_VALUETYPE_STRING
                Size = 34
                Data = "com.sun.star.loader.SharedLibrary"
 
       / SERVICES
         / org.openoffice.helloWorld
       / LOCATION
         Value: Type = RG_VALUETYPE_STRING
                Size = 18
                Data = "libhworld680li.so"
...
...
...
/ SERVICES
...
...
...
   / org.openoffice.helloWorld
     Value: Type = RG_VALUETYPE_STRINGLIST
            Size = 35
            Len  = 1
            Data = 0 = "org.openoffice.world.hello"

这次我们在charmap中运行代码,注册表中有了我们服务的相应入口,成功的返回正确的服务实例。然后去执行。

同样,regcomp -revoke -r services.rdb -c libhworld680li.so 可以移去注册表的入口,我们将再次得到错误信息。:-) 注意services.rdb通常只读安装,为了write/register/revoke注册表,必须修改他的权限。

留给我们的最后一件事情是,在安装时自动注册组件。同拷贝文件一样,已经存在的架构使我们只要做两处修改就可以方便的实现

--- scp2/source/ooo/file_library_ooo.scp        2005-07-23 14:31:19.997821619 +0530
+++ scp2/source/ooo/file_library_ooo.scp        2005-07-23 14:31:54.521173943 +0530
@@ -469,7 +469,8 @@ End
 #ifdef UNX
 File gid_File_Lib_Hl_World
     TXT_FILE_BODY;
-    Styles = (PACKED);
+    Styles = (PACKED,UNO_COMPONENT);
+    RegistryID = gid_Starregistry_Services_Rdb;
     Dir = gid_Dir_Program;
     Name = STRING(CONCAT4(libhworld,OFFICEUPD,DLLSUFFIX,UNXSUFFIX));
 End

到这里就结束了, 谢谢你 ? :-)

 

 

 

From OpenOffice.org Wiki


下一步该做什么? :-) 我们已经实现了一个已存在的接口,这个接口强迫我们实现了一个方法,上一讲演示了组件如何使用, 我们也可以做自己的接口,然后实现。这就更酷了! :-)

通常我们可以借鉴已存在的接口。这次我们使用XExecutableDialog来转换为我们想要的接口XHelloWorldDailog. 第一步可以做出我们接口的idl文件:

--- offapi/com/sun/star/ui/dialogs/XExecutableDialog.idl        2004-06-04 07:19:19.000000000 +0530
+++ offapi/com/sun/star/ui/dialogs/XHelloWorldDialog.idl        2005-07-21 14:46:31.000000000 +0530
@@ -59,8 +59,8 @@
  *
  ************************************************************************/
 
-#ifndef __com_sun_star_ui_dialogs_XExecutableDialog_idl__
-#define __com_sun_star_ui_dialogs_XExecutableDialog_idl__
+#ifndef __com_sun_star_ui_dialogs_XHelloWorldDialog_idl__
+#define __com_sun_star_ui_dialogs_XHelloWorldDialog_idl__
 
 #ifndef __com_sun_star_uno_RuntimeException_idl__
 #include
@@ -79,25 +79,15 @@
 */
  
 
-published interface XExecutableDialog: com::sun::star::uno::XInterface
+published interface XHelloWorldDialog: com::sun::star::uno::XInterface
 {
        //-------------------------------------------------------------------------
-       /**     Sets the title of the dialog.
-
-               @param aTitle
-               Set an abitrary title for the dialog,
-               may be an empty string if the dialog should not
-               have a title.
-       */
-       void setTitle( [in] string aTitle );
-
-
//-------------------------------------------------------------------------
        /**     Executes (shows) the dialog.
                                                                                                                 
                @returns
                A status code of type ExecutableDialogResults.
        */
-       short execute();
+       void adios();
 };
                                                                                                                 
 //=============================================================================
添加了我们所需要的adios函数。 :-D

这是一个idl文件,我们需要一个实现可以使用的头文件,所以我们首先在offapi/目录中'build',这可以更新类型/接口的数据库, 然后可以在offuh/中产生可以使用的头文件。为了offuh/ 拥有更新了的类型数据库, 我们在offapi/中‘deliver’新编译的工程:

raul@lumbini:~/m110/ooo-build/build/hack.src680-m110/offapi> deliver
deliver -- version: 1.89
COPY: ../unxlngi4.pro/ucr/offapi.db -> /home/raul/m110/ooo-build/build/hack.src680-m110//solver/680/unxlngi4.pro/bin/offapi.rdb
COPY: ../unxlngi4.pro/ucrdoc/offapi_doc.db -> /home/raul/m110/ooo-build/build/hack.src680-m110//solver/680/unxlngi4.pro/bin/offapi_doc.rdb
COPY: ../com/sun/star/ui/dialogs/XHelloWorldDialog.idl -> /home/raul/m110/ooo-build/build/hack.src680-m110//solver/680/unxlngi4.pro/idl/com/sun/star/ui/dialogs/XHelloWorldDialog.idl
LOG: writing /home/raul/m110/ooo-build/build/hack.src680-m110//solver/680/unxlngi4.pro/inc/offapi/deliver.log
Statistics:
Files copied: 3
Files unchanged/not matching: 2969

然后在offuh/中'build',这产生我们可以使用的XHelloWorldDialog.hpp文件:

raul@lumbini:~/m110/ooo-build/build/hack.src680-m110/offuh> build
build -- version: 1.140
 
-----------------------------------------------
Building project offuh
-----------------------------------------------
/home/raul/m110/ooo-build/build/hack.src680-m110/offuh/source
cppumaker -Gc -L -BUCR -O../unxlngi4.pro/inc
/home/raul/m110/ooo-build/build/hack.src680-m110//solver/680/unxlngi4.pro/bin/types.rdb

&& echo > ../unxlngi4.pro/misc/offuh.don

输入'deliver',这将拷贝新的头文件到solver/目录下,然后其他工程就可以使用这些头文件了:

raul@lumbini:~/m110/ooo-build/build/hack.src680-m110/offuh> deliver
deliver -- version: 1.89
COPY: ../unxlngi4.pro/inc/com/sun/star/ui/dialogs/XHelloWorldDialog.hdl -> /home/raul/m110/ooo-build/build/hack.src680-m110//solver/680/unxlngi4.pro/inc/com/sun/star/ui/dialogs/XHelloWorldDialog.hdl
COPY: ../unxlngi4.pro/inc/com/sun/star/ui/dialogs/XHelloWorldDialog.hpp -> /home/raul/m110/ooo-build/build/hack.src680-m110//solver/680/unxlngi4.pro/inc/com/sun/star/ui/dialogs/XHelloWorldDialog.hpp
LOG: writing /home/raul/m110/ooo-build/build/hack.src680-m110//solver/680/unxlngi4.pro/inc/offuh/deliver.log
Statistics:
Files copied: 2
Files unchanged/not matching: 4544

现在我们可以返回到实现代码处:

--- helloworld/inc/helloworld.hxx       2005-07-20 13:26:05.000000000 +0530
+++ helloworld/inc/helloworld.hxx       2005-07-21 14:51:26.000000000 +0530
@@ -16,8 +16,8 @@
 #ifndef _COM_SUN_STAR_REGISTRY_XREGISTRYKEY_HPP_
 #include
 #endif
-#ifndef _COM_SUN_STAR_UI_DIALOGS_XEXECUTABLEDIALOG_HPP_
-#include
+#ifndef _COM_SUN_STAR_UI_DIALOGS_XHELLOWORLDDIALOG_HPP_
+#include
 #endif
 
 using namespace rtl;
@@ -25,27 +25,22 @@ using namespace com::sun::star::uno;
 
 namespace hello {
 
        namespace world {
 
-       class HelloWorld : public WeakImplHelper1< XExecutableDialog > {
+       class HelloWorld : public WeakImplHelper1< XHelloWorldDialog > {
 
                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();
+               virtual void SAL_CALL adios()
+                       throw( RuntimeException );

                };
        };

我们使用XHelloWorldDialog替换XExecutableDialog,根据头文件中的方法,实现文件也类似:

--- helloworld/source/helloworld.cxx    2005-07-20 13:33:52.000000000 +0530
+++ helloworld/source/helloworld.cxx    2005-07-21 14:51:33.000000000 +0530
@@ -10,27 +10,16 @@ HelloWorld::HelloWorld( const Reference<
 {
 }
 
-void HelloWorld::adios()
+void SAL_CALL HelloWorld::adios() throw( RuntimeException )
 {
     fprintf( stderr, "Hello, World! :-)\n" );
 }
 
-// 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" );
-}
-
 // UNO component instantiator class
 Reference< XInterface > createHelloWorld(
        const Reference< XMultiServiceFactory > & xMgr )
 {
-       return Reference< XInterface >( static_cast< XExecutableDialog* >( new HelloWorld( xMgr ) ) );
+       return Reference< XInterface >( static_cast< XHelloWorldDialog* >( new HelloWorld( xMgr ) ) );
 }
 
 // UNO registration and invocation
And there, we're grandly back to square one! :-)

重新回到svx/,也是同样改变用法:

--- svx/source/dialog/charmap.cxx       2004-07-13 15:15:11.000000000 +0530
+++ svx/source/dialog/charmap.cxx       2005-07-21 14:54:49.000000000 +0530
@@ -61,6 +61,8 @@
 
 // include ---------------------------------------------------------------
 
+#include
+
 #include
 
 #define _SVX_CHARMAP_CXX_
@@ -117,6 +119,9 @@

 #ifndef _COMPHELPER_TYPES_HXX_
 #include

 #endif
+#ifndef _UNOTOOLS_PROCESSFACTORY_HXX
+#include
+#endif
 
 #include "rtl/ustrbuf.hxx"
 
@@ -1097,6 +1102,15 @@ void SvxCharMapData::SetCharFont( const
 
 IMPL_LINK( SvxCharMapData, OKHdl, OKButton *, EMPTYARG )
 {
+    Reference< XHelloWorldDialog > xHelloWorld( ::comphelper::getProcessServiceFactory()->
+        createInstance( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("org.openoffice.helloWorld") ) ), UNO_QUERY );
+
+    if( xHelloWorld.is() )
+        xHelloWorld->adios();
+    else
+        fprintf( stderr, "Unable to instantiate xHelloWorld.\n" );
+
+
     String aStr = aShowText.GetText();
 
     if ( !aStr.Len() )

有个小问题是需要新的头文件,所以在svx/编译之前,更新的helloworod.hxx和库需要'deliver'.

The existing infrastructure within OOo for UNO takes care of all the multiple stages otherwise required in between to get a UNO component up so that we can focus on what matters most - the functionality! Adios :-)

posted @ 2010-10-21 09:35  MayFirst  阅读(2359)  评论(0编辑  收藏  举报