• 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 :-)

  • 相关阅读:
    第19篇 2016年计划
    第18篇 我的中国梦
    Linux中文件实时同步
    Ansible Playbook
    Ansible简介及常用模块
    HTTP协议简单认识
    zabbix 分布式监控Proxy
    Zabbix中Agent自动注册
    Groovy基础语法
    Python文件操作
  • 原文地址:https://www.cnblogs.com/zhyryxz/p/1857224.html
Copyright © 2020-2023  润新知