你知道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 :-)