导航

<2005年7月>
262728293012
3456789
10111213141516
17181920212223
24252627282930
31123456

随笔分类

随笔档案

文章档案

相册

COM组件设计与应用(九)
IDispatch 接口 for vc6.0

下载源代码

一、前言
    终于写到了第九回,我也一直期盼着写这回的内容耶,为啥呢?因为自动化(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 都帮我们已经完成了。本回我们着重介绍组件的编写,下回则介绍组件的调用方法。

三、用 MFC 实现自动化组件
    我写的这整个系列文章---《COM 组件设计与应用》,多是用 ATL 写组件程序,但由于自动化非常有用,在后续的文章中,还要给大家介绍组件的“事件”功能,还要介绍如何在 MFC 的程序中象 WORD 一样支持“宏”的功能。这些都要用到 MFC,所以就给读者唠一唠啦:-)
    3-1:建立一个工作区(Workspace)
    3-2:建立一个 MFC DLL 工程(Project),工程名称为“Simple5”

    3-3:一定要选择 automation,切记!切记!

    3-4:建立新类

    3-5:在新建类中支持automation

Class information - Name 你随便写个类名子啦
Class information - Base class 一定要从 CComTarget 派生呀,只有它才提供了 IDispatch 的支持
Automation - None 表示不支持自动化,你要选择了它,那就白干啦
Automation - Automation 支持自动化,但不能被直接实例化。后面在讲解多个 IDispatch 的时候就用到它了,现在先不要着急。
Automation - Createable by type ID 一定要选择这个项目,这样我们在后面的调用中,VB就能够CreateObject(),VC就能够CreateDispatch()对组件对象实例化了。注意一点,这个 ID 其实就是组件的 ProgID 啦。
    3-6:启动 ClassWizard,选择 Automation 卡片,准备建立函数

    3-7:添加函数。我们要写一个整数加法函数Add()。

    3-8:再增加一个转换字符串大小写的函数 Upper()。函数返回值是 BSTR,这个没有什么疑问,但参数类型怎么居然是 LPCTSTR?在 COM 中,字符串不是应该使用 BSTR 吗?是的,是应该使用 BSTR,但由于我们是用 MFC 写自动化组件,它帮我们进行 BSTR 和 LPCTSTR 之间的转换了。

    3-9:好了,下面开始输入程序代码:
long CDispSimple::Add(long n1, long n2) 
{
	return n1 + n2;
}

BSTR CDispSimple::Upper(LPCTSTR str) 
{
	CString strResult(str);
	strResult.MakeUpper();

	return strResult.AllocSysString();
}
    3-10:编译注册
    如果上面的操作由于疏忽而发生了错误,那么你可以手工进行改正。
其一、步骤<3-6>的对话窗中有“Delete”操作;
其二、你可以打开 ODL 文件(注2)进行修改,修改时要特别小心函数的声明中,有一个[id(n)] 的函数序号,可不要乱了;
其三、同步修改 H/CPP 中的函数声明和函数体;
其四、在CPP文件中,根据情况也要修改 BEGIN_DISPATCH_MAP/END_DISPATCH_MAP()函数影射宏。
    正确编译后,MFC不象ATL那样会自动注册。你需要手工执行 regsvr32.exe 进行注册,或者执行菜单“Tools\Register control”

四、用 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,那么我们就可以实现函数的可变参数类型呀。怪怪个咙,真爽!

五、脚本中调用举例
    打开“记事本”程序,输入脚本程序,保存为 xxx.vbs 文件。然后在资源管理器里就可以双击运行啦。

如果你有能力,也可以用 JScript 书写上面的程序,然后保存为 xxx.js 文件,同样也可以在资源管理器里运行。另外需要说明的一点是,脚本程序文件的图标(win 2000下)是,如果你不是这样的(有一个软件叫“XX 解霸”。写这款软件的人水平太低,它居然使用 .vbs 的扩展名文件作为它的数据流文件,破坏了系统默认的文件类型影射模式,咳......),那么需要重新设置,方法是:


六、WORD 中使用举例
    6-1:录制一段宏程序



    6-2:选择“键盘”,当然你也可以把这个“宏”程序放到“工具栏”上去。这里我们随便指定一个快捷键,比如Ctrl+Z

    6-3:开始录制了,下面你随便输入点什么东东。然后点“停止”
    6-4:接下来,我们执行菜单,选择这个刚刚录制的宏,然后编辑它

    6-5:点“编辑”按钮,输入下面的程序:

不做解释了,你如果会一点点 VB ,就能看懂这个东东哈。然后保存关闭 VBA 的编辑器(注4)。
    6-6:执行啦,执行啦,看看有什么效果呀......
然后按快捷键Ctrl+Z
    你已经扩展了 MS WORD 的功能啦,嘿啦啦啦啦,嘿啦啦啦,天空出彩霞呀......我们只是举了一个简单的例子,其实这个例子并没有什么实际应用的意义,因为人家 WORD 本身就有大小写转换功能。但通过这个小例子,你可以体会出自动化组件的功能了,有够厉害吧?!

七、小结
    没小结!嘿嘿......上当喽:-)
注1:以后我们描述接口函数,都采用 IDL 的形式了。
注2:ODL 文件和 IDL 类似,是MFC专门为自动化而描述的接口文件
注3:双接口,是支持 IDispatch 接口的一种特殊接口方式,后面马上就要讲啦
注4:VBA 是专门开发 Office 的一种语言---Visual Basic for Application
posted on 2005-07-30 00:47 杨老师的茅屋 阅读(1888) 评论(10)  编辑 收藏
评论
  • # re: 《COM 组件设计与应用(九)》- IDispatch (vc6)原文发表在vckbase中
    freedk
    Posted @ 2005-07-30 09:13
    又是沙发~~~~~~~舒服
  • # re: 杨老师是属兔子的吧?
    pAnic
    Posted @ 2005-07-30 10:36
    一窝生九只哈~:P
  • # re: 《COM 组件设计与应用(九)》- IDispatch (vc6)原文发表在vckbase中
    HateMath
    Posted @ 2005-07-30 23:51
    逐句地看完这个文章以后,我的心久久不能平静,震撼啊!为什么会有如此好的文章!我纵横网络bbs多年,自以为再也不会有任何文章能打动我,没想到今天看到了如此精妙绝伦的这样一篇文章。楼主,是你让我深深地理解了‘人外有人,天外有天’这句话。谢谢侬!在看完这文章以后,我没有立即回复,因为我生怕我庸俗不堪的回复会玷污了这网上少有的文章。但是我还是回复了,因为觉得如果不能在如此精彩的文章后面留下自己的网名,那我死也不会瞑目的!能够在如此精彩的文章后面留下自己的网名是多么骄傲的一件事啊!楼主,请原谅我的自私!我知道无论用多么华丽的辞藻来形容楼主您文章的精彩程度都是不够的,都是虚伪的,所以我只想说一句:您的文章太好看了!我愿意一辈子的看下去!这篇文章构思新颖,题材独具匠心,段落清晰,情节诡异,跌宕起伏,主线分明,引人入胜,平淡中显示出不凡的文学功底,可谓是字字珠玑,句句经典,是我辈应当学习之典范。就小说艺术的角度而言,这篇文章不算太成功,但它的实验意义却远远大于成功本身。正所谓:“一马奔腾,射雕引弓,天地都在我心中!”楼主真不愧为无厘界新一代的开山怪!本来我已经对这个社区失望了,觉得这个社区没有前途了,心里充满了悲哀。但是看了你的这个文章,又让我对社区产生了希望。是你让我的心里重新燃起希望之火,是你让我的心死灰复燃,是你拯救了我一颗拨凉拨凉的心!本来我决定不会在社区回任何文章了,但是看了你的文章,我告诉自己这个文章是一定要回的!这是百年难得一见的好贴啊!苍天有眼啊,让我在优生之年得以观得如此精彩绝伦的文章!
  • # re: 《COM 组件设计与应用(九)》- IDispatch (vc6)原文发表在vckbase中
    taosiming
    Posted @ 2005-08-03 12:48
    您好:
        我是北京邮电的研究生,现在老师的课题需要使用dcom技术。我现在就你所说的idl生成了*_i.c *_p.c,dlldata.c文件后,如何生成proxy/stup的*ps.dll文件?我用的是vc6.0,你说后面介绍,我一直没有看到,请问你还有别的联系方式么?
    我的手机:13810172821
         希望能得到您的指教。谢谢。
  • # re: 《COM 组件设计与应用(九)》- IDispatch (vc6)原文发表在vckbase中
    taosiming
    Posted @ 2005-08-03 12:49
    邮箱:skynew@wleda.com

  • # re: 《COM 组件设计与应用(九)》- IDispatch (vc6)原文发表在vckbase中
    小飞
    Posted @ 2005-08-13 15:25
    不知道是否可以介绍几本比较好介绍COM的书籍!?谢谢!
  • # re: 《COM 组件设计与应用(九)》- IDispatch (vc6)原文发表在vckbase中
    程晖
    Posted @ 2005-08-31 21:09
    好,按惯例每看完一章,帮老师顶一下.
  • # re: 《COM 组件设计与应用(九)》- IDispatch (vc6)原文发表在vckbase中
    小夏
    Posted @ 2006-03-20 12:32
    越来越精彩了
  • # re: 《COM 组件设计与应用(九)》- IDispatch (vc6)原文发表在vckbase中
    冰楠
    Posted @ 2006-05-10 17:55
    顶就一个字
  • # re: 《COM 组件设计与应用(九)》- IDispatch (vc6)原文发表在vckbase中
    Tony Jiang
    Posted @ 2006-11-13 10:13
    very good. Thanks very much!
标题  
姓名  
主页
验证码 *
内容   
  登录  使用高级评论  Top
[使用Ctrl+Enter键可以直接提交]

统计