VC6的IDispatch接口

一、前言
   
    因为自动化(automation)是非常常用、非常有用、非常精彩的一个 COM 功能。由于 WORD、EXCEL 等 OFFICE 软件提供了“宏”的功能,就连我们使用的VC开发环境也提供了“宏”功能,更由于 HTML、ASP、JSP 等都要依靠脚本(Script)的支持,更体现出了自动化接口的重要性。

    如果你使用 vc6.0 的开发环境,请继续阅读。

    如果你使用 vc.net 2003,请阅读下一回。 

  二、IDispatch接口

    如果是编译型语言,那么我们可以让编译器在编译的时候装载类型库,也就是装载接口的描述。在第七回文章当中,我们分别使用了 #include 方法和 #import 方法来实现的。装载了类型库后,编译器就知道应该如何编译接口函数的调用了---这叫“前绑定”。但是,如果想在脚本语言中使用组件,问题就大了,因为脚本语言是解释执行的,它执行的时候不会知道具体的函数地址,怎么办?自动化接口就为此诞生了---“后绑定”。
   
  自动化组件,其实就是实现了 IDispatch 接口的组件。IDispatch 接口有4个函数,解释语言的执行器就通过这仅有的4个函数来执行组件所提供的功能。IDispatch 接口用 IDL 形式说明如下:(注1)

  object, 
  uuid(00020400-0000-0000-C000-000000000046), // IDispatch 接口的 IID = IID_IDispatch 
  pointer_default(unique) 


interface IDispatch : IUnknown 

  typedef [unique] IDispatch * LPDISPATCH; // 转定义 IDispatch * 为 LPDISPATCH 

  HRESULT GetTypeInfoCount([out] UINT * pctinfo); // 有关类型库的这两个函数,咱们以后再说 
  HRESULT GetTypeInfo([in] UINT iTInfo,[in] LCID lcid,[out] ITypeInfo ** ppTInfo); 

  HRESULT GetIDsOfNames( // 根据函数名字,取得函数序号(DISPID) 
  [in] REFIID riid, 
  [in, size_is(cNames)] LPOLESTR * rgszNames, 
  [in] UINT cNames, 
  [in] LCID lcid, 
  [out, size_is(cNames)] DISPID * rgDispId 
  ); 

  [local] // 本地版函数 
  HRESULT Invoke( // 根据函数序号,解释执行函数功能 
  [in] DISPID dispIdMember, 
  [in] REFIID riid, 
  [in] LCID lcid, 
  [in] WORD wFlags, 
  [in, out] DISPPARAMS * pDispParams, 
  [out] VARIANT * pVarResult, 
  [out] EXCEPINFO * pExcepInfo, 
  [out] UINT * puArgErr 
  ); 

  [call_as(Invoke)] // 远程版函数 
  HRESULT RemoteInvoke( 
  [in] DISPID dispIdMember, 
  [in] REFIID riid, 
  [in] LCID lcid, 
  [in] DWORD dwFlags, 
  [in] DISPPARAMS * pDispParams, 
  [out] VARIANT * pVarResult, 
  [out] EXCEPINFO * pExcepInfo, 
  [out] UINT * pArgErr, 
  [in] UINT cVarRef, 
  [in, size_is(cVarRef)] UINT * rgVarRefIdx,  
  [in, out, size_is(cVarRef)] VARIANTARG * rgVarRef 
  ); 

  以上 IDispatch 接口函数的讲解,我们留到后回中进行介绍。如何在组件程序中实现这些函数那?还好,还好,就象 IUnknown 一样,MFC 和 ATL 都帮我们已经完成了。本回我们着重介绍组件的编写,下回则介绍组件的调用方法。

  四、用 ATL 实现双接口组件(操作方法和步骤,请参考《COM 组件设计与应用(五)》)

    4-1:建立一个 ATL 工程(Project),工程名称为“Simple6”

    4-2:按默认进行。选择 DLL 类型、不合并代理和存根代码、不支持MFC、不支持MTS

    4-3:New Atl Object... 选择Simple Object

    4-4:输入名称和属性,属性按默认进行,也就是 dual(双接口)方式(注3)




   
  4-5:增加函数。在 ClassView 卡片中,选择接口、鼠标右键菜单、Add Method...

Add([in] VARIANT v1, [in] VARIANT v2, [out, retval] VARIANT * pVal);
Upper([in] BSTR str, [out,retval] BSTR * pVal);
   
  关于Add()函数,你依然可以使用 Add([in] long n1, [in] long n2, [out,retval] long * pVal) 方式。但这次我们没有使用 long ,而是使用了 VARIANT 做参数和返回值。这里我先卖个关子,往下看,就知道使用 VARIANT 的精彩之处了。
   
  4-6:完成代码 
STDMETHODIMP CDispSimple::Add(VARIANT v1, VARIANT v2, VARIANT *pVal) 

 ::VariantInit( pVal ); // 永远初始化返回值是个好习惯 

 CComVariant v_1( v1 ); 
 CComVariant v_2( v2 ); 

 if((v1.vt & VT_I4) && (v2.vt & VT_I4) ) // 如果都是整数类型 
 { // 这里比较没有使用 == ,而使用了运算符 & ,你知道这是为什么吗? 
  v_1.ChangeType( VT_I4 ); // 转换为整数 
  v_2.ChangeType( VT_I4 ); // 转换为整数 

  pVal->vt = VT_I4; 
  pVal->lVal = v_1.lVal + v_2.lVal; // 加法 
 } 
 else 
 { 
  v_1.ChangeType( VT_BSTR ); // 转换为字符串 
  v_2.ChangeType( VT_BSTR ); // 转换为字符串 

  CComBSTR bstr( v_1.bstrVal ); 
  bstr.AppendBSTR( v_2.bstrVal ); // 字符串连接 

  pVal->vt = VT_BSTR; 
  pVal->bstrVal = bstr.Detach(); 
 } 
 return S_OK; 


STDMETHODIMP CDispSimple::Upper(BSTR str, BSTR *pVal) 

 *pVal = NULL; // 永远初始化返回值是个好习惯 

 CComBSTR s(str); 
 s.ToUpper(); // 转换为大写 

 *pVal = s.Copy(); 

 return S_OK; 

    刚才卖的关子,现在开始揭密了......加法函数Add()不使用long类型,而使用VARIANT的好处是:函数内部动态判断参数类型,如果是整数则进行整数加法,如果是字符串,则进行字符串加法(字符串加法就是字符串连接哈)。也就是说,如果参数是VARIANT,那么我们就可以实现函数的可变参数类型呀。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值