|
|
Line 2: |
Line 2: |
| |WritingUNO2d=block | | |WritingUNO2d=block |
| |ShowPrevNext=block | | |ShowPrevNext=block |
− | |PrevPage=Zh/Documentation/DevGuide/WritingUNO/C++/Provide Implementation Environment | + | |PrevPage=Zh/Documentation/DevGuide/WritingUNO/C++/Write Registration Info Using a Helper Method |
− | |NextPage=Zh/Documentation/DevGuide/WritingUNO/C++/Storing the Service Manager for Further Use | + | |NextPage=Zh/Documentation/DevGuide/WritingUNO/C++/Implementing without Helpers |
| }} | | }} |
− | [[en:Documentation/DevGuide/WritingUNO/C++/Implementing without Helpers]] | + | [[en:Documentation/DevGuide/WritingUNO/C++/Provide Implementation Environment]] |
− | {{DISPLAYTITLE:不使用 Helper 的实现}} | + | {{DISPLAYTITLE:提供实现环境}} |
| | | |
− | __NOTOC__
| |
| | | |
− | 下一节将介绍不使用帮助程序的可能的实现。如果要通过帮助程序模板实现多于规划的接口,这种实现非常有用。帮助程序模板只允许最多十个接口。本节还将介绍核心接口的工作方式。
| + | 名为 <code>component_getImplementationEnvironment()</code> 的函数将通知共享库组件加载程序,建立库时使用了哪一个编译程序。如果使用不同的编译程序编译了不同的组件,则此信息是必需的。一个特定的 C++ 编译程序叫做一个环境。如果使用了不同的编译程序,加载程序必须将接口从一个编译程序环境连接到另一个编译程序环境,以建立对象之间的通信基础结构。必须在 UNO 运行时中安装相应的 C++ 连接。在大多数情况下,上述函数可以用以下方法实现: |
− | | + | |
− | | + | |
− | | + | |
− | === XInterface 实现 ===
| + | |
− | | + | |
− | | + | |
− | 对象生命周期通过通用基接口 <idl>com.sun.star.uno.XInterface</idl> 的方法 <code>acquire()</code> 和 <code>release()</code> 来进行控制。这两种方法使用'''引用计数'''实现,即每使用一次 <code>acquire()</code>,计数器递增,每使用一次 <code>release()</code>,计数器递减。最后一次递减之后,对象“死亡”。在线程安全的模式下编程时,此计数器成员变量的修改通常通过一对名为 <code>osl_incrementInterlockedcount()</code> 和 <code>osl_decrementInterlockedcount()</code>(包括 '''osl/interlck.h''')的 sal 库函数执行。
| + | |
− |
| + | |
− | <!--[SOURCE:Components/CppComponent/service1_impl.cxx]-->
| + | |
− | {{Documentation/Caution|请注意编写代码时的符号冲突。将代码封装到一个单独的名称空间(例如 "<tt>my_sc_impl</tt>")是很常用的方式。问题是,在 Unix 上加载您的共享库时,运行时期间可能会发生符号冲突。}}
| + | |
| | | |
| + | <!--[SOURCE:Components/CppComponent/service2_impl.cxx]--> |
| <source lang="cpp"> | | <source lang="cpp"> |
− | namespace my_sc_impl | + | extern "C" void SAL_CALL component_getImplementationEnvironment( |
| + | sal_Char const ** ppEnvTypeName, uno_Environment ** ppEnv ) |
| { | | { |
− | class MyService1Impl
| + | *ppEnvTypeName = CPPU_CURRENT_LANGUAGE_BINDING_NAME; |
− | ... | + | |
− | {
| + | |
− | oslInterlockedCount m_refcount;
| + | |
− | public:
| + | |
− | inline MyService1Impl() throw ()
| + | |
− | : m_refcount( 0 )
| + | |
− | {}
| + | |
− |
| + | |
− | // XInterface
| + | |
− | virtual Any SAL_CALL queryInterface( Type const & type )
| + | |
− | throw (RuntimeException);
| + | |
− | virtual void SAL_CALL acquire()
| + | |
− | throw ();
| + | |
− | virtual void SAL_CALL release()
| + | |
− | throw ();
| + | |
− | ...
| + | |
− | };
| + | |
− | void MyService1Impl::acquire()
| + | |
− | throw ()
| + | |
− | {
| + | |
− | // thread-safe incrementation of reference count
| + | |
− | ::osl_incrementInterlockedCount( &m_refcount );
| + | |
− | }
| + | |
− | void MyService1Impl::release()
| + | |
− | throw ()
| + | |
− | {
| + | |
− | // thread-safe decrementation of reference count
| + | |
− | if (0 == ::osl_decrementInterlockedCount( &m_refcount ))
| + | |
− | {
| + | |
− | delete this; // shutdown this object
| + | |
− | }
| + | |
| } | | } |
| </source> | | </source> |
| | | |
| + | 宏 <code>CPPU_CURRENT_LANGUAGE_BINDING_NAME</code> 是一个由编译环境定义的 C 字符串(如果您使用 SDK 编译环境)。例如,使用 Microsoft Visual C++ 编译程序编译时,它被定义为 "<code>msci</code>",但是使用 GNU gcc 3 编译时,则被定义为 "<code>gcc3</code>"。 |
| | | |
− | 在 <code>queryInterface()</code> 方法中,接口指针必须提供给对象的接口。这意味着,将 this 类型分别转换为由接口的 '''cppumaker''' 工具生成的纯粹的 C++ 虚类。必须返回所有支持的接口,包括已继承的接口,如 <code>XInterface</code>。
| |
| | | |
− | <!--[SOURCE:Components/CppComponent/service1_impl.cxx]-->
| |
− | <source lang="cpp">
| |
− | Any MyService1Impl::queryInterface( Type const & type )
| |
− | throw (RuntimeException)
| |
− | {
| |
− | if (type.equals( ::cppu::UnoType< Reference< XInterface > >::get()))
| |
− | {
| |
− | // return XInterface interface (resolve ambiguity caused by multiple inheritance from
| |
− | // XInterface subclasses by casting to lang::XTypeProvider)
| |
− | Reference< XInterface > x( static_cast< lang::XTypeProvider * >( this ) );
| |
− | return makeAny( x );
| |
− | }
| |
− | if (type.equals( ::cppu::UnoType< Reference< lang::XTypeProvider > >::get()))
| |
− | {
| |
− | // return XInterface interface
| |
− | Reference< XInterface > x( static_cast< lang::XTypeProvider * >( this ) );
| |
− | return makeAny( x );
| |
− | }
| |
− | if (type.equals(( ::cppu::UnoType< Reference< lang::XServiceInfo > >::get()))
| |
− | {
| |
− | // return XServiceInfo interface
| |
− | Reference< lang::XServiceInfo > x( static_cast< lang::XServiceInfo * >( this ) );
| |
− | return makeAny( x );
| |
− | }
| |
− | if (type.equals( ::cppu::UnoType< Reference< ::my_module::XSomething > >::get()))
| |
− | {
| |
− | // return sample interface
| |
− | Reference< ::my_module::XSomething > x( static_cast< ::my_module::XSomething * >( this ) );
| |
− | return makeAny( x );
| |
− | }
| |
− | // querying for unsupported type
| |
− | return Any();
| |
− | }
| |
− | </source>
| |
− |
| |
− | === XTypeProvider 实现 ===
| |
− |
| |
− |
| |
− | 实现 <idl>com.sun.star.lang.XTypeProvider</idl> 接口时,有两个方法必须编码。第一个是 <code>getTypes()</code>,提供实现的所有实现的类型,不包括基本类型,例如 <idl>com.sun.star.uno.XInterface</idl>。第二个是 <code>getImplementationId()</code>,提供此组接口的唯一ID。上文提到的线程安全的实现如下所示:
| |
− |
| |
− | <!--[SOURCE:Components/CppComponent/service1_impl.cxx]-->
| |
− | <source lang="cpp">
| |
− | Sequence< Type > MyService1Impl::getTypes()
| |
− | throw (RuntimeException)
| |
− | {
| |
− | Sequence< Type > seq( 3 );
| |
− | seq[ 0 ] = ::cppu::UnoType< Reference< lang::XTypeProvider > >::get();
| |
− | seq[ 1 ] = ::cppu::UnoType< Reference< lang::XServiceInfo > >::get();
| |
− | seq[ 2 ] = ::cppu::UnoType< Reference< ::my_module::XSomething > >::get();
| |
− | return seq;
| |
− | }
| |
− | Sequence< sal_Int8 > MyService1Impl::getImplementationId()
| |
− | throw (RuntimeException)
| |
− | {
| |
− | static Sequence< sal_Int8 > * s_pId = 0;
| |
− | if (! s_pId)
| |
− | {
| |
− | // create unique id
| |
− | Sequence< sal_Int8 > id( 16 );
| |
− | ::rtl_createUuid( (sal_uInt8 *)id.getArray(), 0, sal_True );
| |
− | // guard initialization with some mutex
| |
− | ::osl::MutexGuard guard( ::osl::Mutex::getGlobalMutex() );
| |
− | if (! s_pId)
| |
− | {
| |
− | static Sequence< sal_Int8 > s_id( id );
| |
− | s_pId = &s_id;
| |
− | }
| |
− | }
| |
− | return *s_pId;
| |
− | }
| |
− | </source>
| |
− |
| |
− |
| |
− | {{Documentation/Caution|通常,如果不知道调用的代码在做什么,调用陌生代码时,不要 <tt>acquire()</tt> 互斥对象(Mutex)。您永远不会知道陌生代码获取什么样的互斥对象,而这可以导致死锁。这就是为什么在获取初始化互斥对象之前要创建'''后一个值''' (uuid)。成功获取互斥对象后,将再次检查 s_pID 的值并进行指定(如果以前未指定)。这就是“双重检查锁定”设计模式。
| |
− |
| |
− |
| |
− | 以上实现 ID 的初始化操作在特定平台不能正常运行。如果需要实现此操作的更佳方法,请参阅 [[Zh/Documentation/DevGuide/AdvUNO/UNO Design Patterns and Coding Styles#Double-Checked Locking|高级UNO - 设计模式 - 双检测锁定]]。}}
| |
− |
| |
− | === 提供单个工厂 ===
| |
− |
| |
− |
| |
− | 函数 <code>component_getFactory()</code> 为请求的实现提供单个对象工厂,以创建其中某个服务实现的对象实例。使用 '''cppuhelper/factory.hxx''' 的帮助程序,这可以通过以下代码快速实现:
| |
− |
| |
− | <!--[SOURCE:Components/CppComponent/service1_impl.cxx]-->
| |
− | <source lang="cpp">
| |
− | #include <cppuhelper/factory.hxx>
| |
− |
| |
− | namespace my_sc_impl
| |
− | {
| |
− | ...
| |
− | static Reference< XInterface > SAL_CALL create_MyService1Impl(
| |
− | Reference< XComponentContext > const & xContext )
| |
− | SAL_THROW( () )
| |
− | {
| |
− | return static_cast< lang::XTypeProvider * >( new MyService1Impl() );
| |
− | }
| |
− | static Reference< XInterface > SAL_CALL create_MyService2Impl(
| |
− | Reference< XComponentContext > const & xContext )
| |
− | SAL_THROW( () )
| |
− | {
| |
− | return static_cast< lang::XTypeProvider * >( new MyService2Impl() );
| |
− | }
| |
− | }
| |
− |
| |
− | extern "C" void * SAL_CALL component_getFactory(
| |
− | sal_Char const * implName, lang::XMultiServiceFactory * xMgr, void * )
| |
− | {
| |
− | Reference< lang::XSingleComponentFactory > xFactory;
| |
− | if (0 == ::rtl_str_compare( implName, "my_module.my_sc_impl.MyService1" ))
| |
− | {
| |
− | // create component factory for MyService1 implementation
| |
− | OUString serviceName( RTL_CONSTASCII_USTRINGPARAM("my_module.MyService1") );
| |
− | xFactory = ::cppu::createSingleComponentFactory(
| |
− | ::my_sc_impl::create_MyService1Impl,
| |
− | OUString( RTL_CONSTASCII_USTRINGPARAM("my_module.my_sc_impl.MyService1") ),
| |
− | Sequence< OUString >( &serviceName, 1 ) );
| |
− | }
| |
− | else if (0 == ::rtl_str_compare( implName, "my_module.my_sc_impl.MyService2" ))
| |
− | {
| |
− | // create component factory for MyService12 implementation
| |
− | OUString serviceName( RTL_CONSTASCII_USTRINGPARAM("my_module.MyService2") );
| |
− | xFactory = ::cppu::createSingleComponentFactory(
| |
− | ::my_sc_impl::create_MyService2Impl,
| |
− | OUString( RTL_CONSTASCII_USTRINGPARAM("my_module.my_sc_impl.MyService2") ),
| |
− | Sequence< OUString >( &serviceName, 1 ) );
| |
− | }
| |
− | if (xFactory.is())
| |
− | xFactory->acquire();
| |
− | return xFactory.get(); // return acquired interface pointer or null
| |
− | }
| |
− | </source>
| |
− |
| |
− |
| |
− | 在以上的示例中,请注意在函数 <code>::my_sc_impl::create_MyService1Impl()</code> 需要实例化类时,它被工厂对象调用。组件上下文 <idl>com.sun.star.uno.XComponentContext</idl> 提供给该函数,并且可能会传递给 <code>MyService1Impl</code> 的构造函数。
| |
− |
| |
− | === 编写注册信息 ===
| |
− |
| |
− |
| |
− | 在将组件注册到注册表数据库文件 ('''.rdb''') 时,共享库组件加载程序将调用函数 <code>component_writeInfo()</code>。当 '''regcomp''' 调用注册表时,组件将有关它可以实例化的对象的信息写入注册表中。
| |
− |
| |
− | <!--[SOURCE:Components/CppComponent/service1_impl.cxx]-->
| |
− | <source lang="cpp">
| |
− | extern "C" sal_Bool SAL_CALL component_writeInfo(
| |
− | lang::XMultiServiceFactory * xMgr, registry::XRegistryKey * xRegistry )
| |
− | {
| |
− | if (xRegistry)
| |
− | {
| |
− | try
| |
− | {
| |
− | // implementation of MyService1A
| |
− | Reference< registry::XRegistryKey > xKey(
| |
− | xRegistry->createKey( OUString( RTL_CONSTASCII_USTRINGPARAM(
| |
− | "my_module.my_sc_impl.MyService1/UNO/SERVICES") ) ) );
| |
− | // subkeys denote implemented services of implementation
| |
− | xKey->createKey( OUString( RTL_CONSTASCII_USTRINGPARAM(
| |
− | "my_module.MyService1") ) );
| |
− | // implementation of MyService1B
| |
− | xKey = xRegistry->createKey( OUString( RTL_CONSTASCII_USTRINGPARAM(
| |
− | "my_module.my_sc_impl.MyService2/UNO/SERVICES") ) );
| |
− | // subkeys denote implemented services of implementation
| |
− | xKey->createKey( OUString( RTL_CONSTASCII_USTRINGPARAM(
| |
− | "my_module.MyService2") ) );
| |
− | return sal_True; // success
| |
− | }
| |
− | catch (registry::InvalidRegistryException &)
| |
− | {
| |
− | // function fails if exception caught
| |
− | }
| |
− | }
| |
− | return sal_False;
| |
− | }
| |
− | </source>
| |
| {{PDL1}} | | {{PDL1}} |
| | | |
| [[Category:文档/开发者指南/编写 UNO 组件]] | | [[Category:文档/开发者指南/编写 UNO 组件]] |