【转载】COM 组件设计与应用(十)——IDispatch 接口 for VC.NET
原文:http://vckbase.com/index.php/wv/1225.html
一、前言
终于写到了第十回,我也一直期盼着写这回的内容耶,为啥呢?因为自动化(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:建立一个解决方案
3-2:建立一个 MFC DLL 项目,项目名称为“Simple7”
3-3:一定要选择附加功能中的“自动化”,切记!切记!
3-4:添加新类
3-5:在新建类中支持自动化
类名 你随便写个类名子啦
基类 一定要从 CComTarget 派生呀,只有它才提供了 IDispatch 的支持
自动化 - 无 表示不支持自动化,你要选择了它,那就白干啦
自动化 - 自动化 支持自动化,但不能被直接实例化。后面在讲解多个 IDispatch 的时候就用到它了,现在先不要着急。
自动化 - 可按类型ID创建 一定要选择这个项目,这样我们在后面的调用中,VB就能够CreateObject(),VC就能够CreateDispatch()对组件对象实例化了。注意一点,这个 ID 其实就是组件的 ProgID 啦。
3-6:选择接口,添加函数
3-7:添加函数。我们要写一个整数加法函数Add()。
3-8:再增加一个转换字符串大小写的函数 Upper()。
3-9:好了,下面开始输入程序代码:
LONG CDispSimple::Add(LONG n1, LONG n2)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState()); return n1 + n2;
} BSTR CDispSimple::Upper(LPCTSTR str)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState()); CString strResult(str);
strResult.MakeUpper(); return strResult.AllocSysString();
}
3-10:编译注册
如果上面的操作由于疏忽而发生了错误,那么你可以手工进行改正。
其一、你可以打开 IDL 文件进行修改,修改时要特别小心函数的声明中,有一个[id(n)] 的函数序号,可不要乱了;
其二、同步修改 H/CPP 中的函数声明和函数体;
其三、在CPP文件中,根据情况也要修改 BEGIN_DISPATCH_MAP/END_DISPATCH_MAP()函数影射宏。
正确编译后,vc.net 2003 比 vc6.0 要聪明多了,它会自动注册组件。如果复制到其它计算机上,你也需要手工执行 regsvr32.exe 进行注册。
四、用 ATL 实现双接口组件(操作方法和步骤,请参考《COM 组件设计与应用(六)》)
4-1:建立一个 ATL 项目,项目名称为“Simple8”
4-2:选择 DLL 类型、非属性方式、不要选择任何附加选项
4-3:添加新类,选择ATL 的简单对象
4-4:输入简称和选项,选项按默认进行,也就是双重接口方式(注2)
4-5:增加函数。选择接口、鼠标右键菜单、添加方法...
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:双接口,是支持 IDispatch 接口的一种特殊接口方式,后面马上就要讲啦
注3:VBA 是专门开发 Office 的一种语言---Visual Basic for Application
【转载】COM 组件设计与应用(十)——IDispatch 接口 for VC.NET的更多相关文章
- xmlplus 组件设计系列之十 - 网格(DataGrid)
这一章我们要实现是一个网格组件,该组件除了最基本的数据展示功能外,还提供排序以及数据过滤功能. 数据源 为了测试我们即将编写好网格组件,我们采用如下格式的数据源.此数据源包含两部分的内容,分别是表头数 ...
- 【转载】COM 组件设计与应用(十一)—— IDispatch 及双接口的调用
原文:http://vckbase.com/index.php/wv/1236.html 一.前言 前段时间,由于工作比较忙,没有能及时地写作.其间收到了很多网友的来信询问和鼓励,在此一并表示感谢.咳 ...
- 【转载】COM 组件设计与应用(十八)——属性包
原文:http://vckbase.com/index.php/wv/1265.html 一.前言 书接上回,本回着落在介绍属性包 IPersistPropertyBag 接口的实现方法和调用方式.属 ...
- 【转载】COM 组件设计与应用(十四)——事件和通知(vc.net)
原文:http://vckbase.com/index.php/wv/1244.html 一.前言 我的 COM 组件运行时产生一个窗口,当用户双击该窗口的时候,我需要通知调用者: 我的 COM 组件 ...
- 【转载】COM 组件设计与应用(九)——IDispatch 接口 for VC6.0
原文: http://vckbase.com/index.php/wv/1224.html 一.前言 终于写到了第九回,我也一直期盼着写这回的内容耶,为啥呢?因为自动化(automation)是非常常 ...
- 【转载】COM 组件设计与应用(十五)——连接点(vc6.0)
原文:http://vckbase.com/index.php/wv/1256.html 一.前言 上回书介绍了回调接口,在此基础上,我们理解连接点就容易多了. 二.原理 图一.连接点组件原理图.左侧 ...
- 【转载】COM 组件设计与应用(十三)——事件和通知(VC6.0)
原文:http://vckbase.com/index.php/wv/1243.html 一.前言 我的 COM 组件运行时产生一个窗口,当用户双击该窗口的时候,我需要通知调用者: 我的 COM 组件 ...
- 【转载】COM 组件设计与应用(八)——实现多接口
原文:http://vckbase.com/index.php/wv/1219.html 一.前言 从第五回开始到第七回,咱们用 ATL 写了一个简单的 COM 组件,之所以说简单,是因为在组件中,只 ...
- 【转载】COM 组件设计与应用(七)——编译、注册、调用
原文:http://vckbase.com/index.php/wv/1218.html 一.前言 上两回中,咱们用 ATL 写了第一个 COM 组件程序,这回中,主要介绍编译.册和调用方法.示例程序 ...
随机推荐
- [VS2015] [asp.net] 允许远程访问本机正在DEBUG的服务
一.打开并编辑解决方案目录(不是工程目录)下的文件: \.vs\config\applicationhost.config 增加黑体行: <site name="XXXX" ...
- 奇怪的等待事件“enq: ss - contention”
数据库有时会遇到大量的进程发生'enq: ss - contention'等待,持续5到10分钟,然后自动消失.从字面上看,'SS'是Sort Segment: select * from v$loc ...
- django 板块动态切换
需求:在同一页面的不同板块上可以实现动态切换,使用一个view实现,具体如下图所示,点击phy显示物理机列表,点击vm显示虚机列表,phy.vm对应的url均是动态生成: ...
- 一、初识MySQL数据库 二、搭建MySQL数据库(重点) 三、使用MySQL数据库 四、认识MySQL数据库的数据类型 五、操作MySQL数据库的数据(重点)
一.初识MySQL数据库 ###<1>数据库概述 1. 数据库 长期存储在计算机内的,由组织的可共享的数据集合 存储数据的仓库 文件 ...
- php函数substr_replace中文乱码的替代解决方法
$str = "中华人民共和国"; $len = mb_strlen($str,'utf-8'); if($len>=6){ $str1 = mb_substr($str,0 ...
- DevExpress01:Bar Manager,bar 、Toolbars
Bar Manager : 不可见的控件 如果想在窗体或用户控件添加工具条或弹出菜单,我们需要把一个不可见的控件 BarManager拖放到这个窗体或用户控件上. 这个BarManager控件维护工具 ...
- 张高兴的 Windows 10 IoT 开发笔记:0.96 寸 I2C OLED
This is a Windows 10 IoT Core project on the Raspberry Pi 2/3, coded by C#. GitHub:https://github.co ...
- Mina使用总结(一)MinaServer
我们先看一个最简单的Mina Server服务端代码,该段代码实现了服务端Server启动并监听客户端请求 package com.bypay.mina.server; import java.io. ...
- idea html热部署
IDEA SpringBoot 热部署+html修改无需make自动刷新 收藏 HeyS1 发表于 3个月前 阅读 310 收藏 2 点赞 0 评论 0 特邀IBM大咖的主题沙龙,5月20日·北京·3 ...
- Result工具类
使用ajax请求访问时,可以用此工具类作为返回对象,也方便统一代码规范 package com.ujia.entity; import java.io.Serializable; public cla ...