调用函数并访问属性

From Apache OpenOffice Wiki
Jump to: navigation, search



Automation 对象的实质是 IDispatch 接口。所有函数调用(包括访问属性)最终需要调用 IDispatch::Invoke。在 C++ 中,IDispatch 使用起来相当麻烦。例如,以下代码调用 createInstance("com.sun.star.reflection.CoreReflection")

 OLECHAR* funcname = L"createInstance";
 DISPID id; 
 IDispatch* pdispFactory= NULL; 
 CLSID clsFactory= {0x82154420,0x0FBF,0x11d4,{0x83, 0x13,0x00,0x50,0x04,0x52,0x6A,0xB4}}; 
 HRESULT hr= CoCreateInstance( clsFactory, NULL, CLSCTX_ALL, __uuidof(IDispatch), (void**)&pdispFactory); 
 
 if( SUCCEEDED(pdispFactory->GetIDsOfNames( IID_NULL, &funcName, 1, LOCALE_USER_DEFAULT, &id)))
 { 
     VARIANT param1; 
     VariantInit( &param1); 
     param1.vt= VT_BSTR; 
     param1.bstrVal= SysAllocString( L"com.sun.star.reflection.CoreReflection"); 
     DISPPARAMS dispparams= { &param1, 0, 1, 0}; 
     VARIANT result; 
     VariantInit( &result); 
     hr= pdispFactory->Invoke( id, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, 
                         &dispparams, &result, NULL, 0); 
 }


首先通过 GetIdsOfNames 获取方法名称 createInstance() 的 COM ID,然后使用该 ID 来 invoke() 方法: createInstance()


在调用 IDispatch 接口中的某个具体函数之前,通过调用 GetIDsOfNames 来获取 DISPID。必要时,由桥生成 DISPID。不存在从成员名称到 DISPID 的固定映射,也就是说,某个对象的第二个实例的同一函数可能具有不同的 DISPID。一旦为某个函数或属性名称创建了一个 DISPID,该 DISPID 在对象的生存期内将保持不变。


Helper 类可以简化该程序。下面的示例说明使用动态模板库中的 Helper 类来实现相同的调用:

 CComDispatchDriver spDisp(pdispFactory);
 CComVariant param(L"com.sun.star.reflection.CoreReflection");
 CComVariant result;
 hr= spUnk.Invoke1(L"createInstance",param, result);


有些框架允许包含 COM 类型库,这是一种在开发期间连接 Automation 对象的简便接口。不能将这些 Helper 类与 UNO 一起使用,因为 SDK 没有提供用于 UNO 组件的 COM 类型库。COM 提供了各种方法来调用 COM 对象中的函数,而 UNO 仅支持 IDispatch


使用 VB 或 JScript 进行 Automation 对象的编程较为容易,因为隐藏了 IDispatch 接口,而且可以直接调用函数。而且,在 VARIANT 中无需包括参数。

 //VB
 Dim objRefl As Object
 Set objRefl= dispFactory.createInstance("com.sun.star.reflection.CoreReflection")
 
 //JScript
 var objRefl= dispFactory.createInstance("com.sun.star.reflection.CoreReflection");


符合以下模式的 get/set 函数对

 SomeType getSomeProperty()
 void setSomeProperty(SomeType aValue)

被作为 COM 对象属性处理。


在 C++ 中访问这类属性与调用方法类似。首先,获取一个 DISPID,然后用正确的参数调用 IDispatch::Invoke

 DISPID dwDispID;
 VARIANT value;
 VariantInit(&value);
 OLECHAR* name= L"AttrByte";
 HRESULT hr = pDisp->GetIDsOfNames(IID_NULL, &name, 1, LOCALE_USER_DEFAULT, &dwDispID);
 if (SUCCEEDED(hr))
 {
     // Get the property
     DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0};
     pDisp->Invoke(dwDispID, IID_NULL,LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET,
           &dispparamsNoArgs, &value, NULL, NULL);
     // The VARIANT value contains the value of the property
 
     // Sset the property
     VARIANT value2;
     VariantInit( value2);
     value2.vt= VT_UI1;
     value2.bval= 10;
 
     DISPPARAMS disparams;
     dispparams.rgvarg = &value2;
     DISPID dispidPut = DISPID_PROPERTYPUT;
     dispparams.rgdispidNamedArgs = &dispidPut;
 
     pDisp->Invoke(dwDispID, IID_NULL,LOCALE_USER_DEFAULT, DISPATCH_PROPERTYPUT,
         &dispparams, NULL, NULL, NULL);
 }


当属性为 IUnknown*、IDispatch*SAFEARRAY* 时,必须使用标志 DISPATCH_PROPERTYPUTREF。当通过引用 (VARIANT.vt = VT_BYREF | ...) 传递值时,情况也是如此。


下面的示例显示使用 ATL Helper 类时,程序看起来比较简单:

 CComVariant prop;
 CComDispatchDriver spDisp( pDisp);
 // get the property
 spDisp.GetPropertyByName(L"AttrByte",&prop);
 //set the property
 CComVariant newVal( (BYTE) 10);
 spDisp.PutPropertyByName(L"AttrByte",&newVal); 


以下示例显示使用 VB 和 JScript 时,程序看起来更简单:

 //VB
 Dim prop As Byte
 prop= obj.AttrByte
 
 Dim newProp As Byte
 newProp= 10
 obj.AttrByte= newProp 
 'or 
 obj.AttrByte= 10
 
 //JScript
 var prop= obj.AttrByte;
 obj.AttrByte= 10;


没有将服务属性映射成 COM 对象属性。使用接口(如 com.sun.star.beans.XPropertySet)来管理服务属性。

Content on this page is licensed under the Public Documentation License (PDL).
Personal tools
In other languages