COM 组件设计与应用09IDispatch 接口VC6.docx
《COM 组件设计与应用09IDispatch 接口VC6.docx》由会员分享,可在线阅读,更多相关《COM 组件设计与应用09IDispatch 接口VC6.docx(14页珍藏版)》请在冰豆网上搜索。
![COM 组件设计与应用09IDispatch 接口VC6.docx](https://file1.bdocx.com/fileroot1/2022-11/16/5888110a-ddc3-43f7-a565-2ae1fb5aa68a/5888110a-ddc3-43f7-a565-2ae1fb5aa68a1.gif)
COM组件设计与应用09IDispatch接口VC6
COM组件设计与应用(九)
IDispatch接口forvc6.0
作者:
杨老师
下载源代码
一、前言
终于写到了第九回,我也一直期盼着写这回的内容耶,为啥呢?
因为自动化(automation)是非常常用、非常有用、非常精彩的一个COM功能。
由于WORD、EXCEL等OFFICE软件提供了“宏”的功能,就连我们使用的VC开发环境也提供了“宏”功能,更由于HTML、ASP、JSP等都要依靠脚本(Script)的支持,更体现出了自动化接口的重要性。
如果你使用vc6.0的开发环境,请继续阅读。
如果你使用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)
]
interfaceIDispatch:
IUnknown
{
typedef[unique]IDispatch*LPDISPATCH;//转定义IDispatch*为LPDISPATCH
HRESULTGetTypeInfoCount([out]UINT*pctinfo);//有关类型库的这两个函数,咱们以后再说
HRESULTGetTypeInfo([in]UINTiTInfo,[in]LCIDlcid,[out]ITypeInfo**ppTInfo);
HRESULTGetIDsOfNames(//根据函数名字,取得函数序号(DISPID)
[in]REFIIDriid,
[in,size_is(cNames)]LPOLESTR*rgszNames,
[in]UINTcNames,
[in]LCIDlcid,
[out,size_is(cNames)]DISPID*rgDispId
);
[local]//本地版函数
HRESULTInvoke(//根据函数序号,解释执行函数功能
[in]DISPIDdispIdMember,
[in]REFIIDriid,
[in]LCIDlcid,
[in]WORDwFlags,
[in,out]DISPPARAMS*pDispParams,
[out]VARIANT*pVarResult,
[out]EXCEPINFO*pExcepInfo,
[out]UINT*puArgErr
);
[call_as(Invoke)]//远程版函数
HRESULTRemoteInvoke(
[in]DISPIDdispIdMember,
[in]REFIIDriid,
[in]LCIDlcid,
[in]DWORDdwFlags,
[in]DISPPARAMS*pDispParams,
[out]VARIANT*pVarResult,
[out]EXCEPINFO*pExcepInfo,
[out]UINT*pArgErr,
[in]UINTcVarRef,
[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:
建立一个MFCDLL工程(Project),工程名称为“Simple5”
3-3:
一定要选择automation,切记!
切记!
3-4:
建立新类
3-5:
在新建类中支持automation
Classinformation-Name你随便写个类名子啦
Classinformation-Baseclass一定要从CComTarget派生呀,只有它才提供了IDispatch的支持
Automation-None表示不支持自动化,你要选择了它,那就白干啦
Automation-Automation支持自动化,但不能被直接实例化。
后面在讲解多个IDispatch的时候就用到它了,现在先不要着急。
Automation-CreateablebytypeID一定要选择这个项目,这样我们在后面的调用中,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:
好了,下面开始输入程序代码:
longCDispSimple:
:
Add(longn1,longn2)
{
returnn1+n2;
}
BSTRCDispSimple:
:
Upper(LPCTSTRstr)
{
CStringstrResult(str);
strResult.MakeUpper();
returnstrResult.AllocSysString();
}
3-10:
编译注册
如果上面的操作由于疏忽而发生了错误,那么你可以手工进行改正。
其一、步骤<3-6>的对话窗中有“Delete”操作;
其二、你可以打开ODL文件(注2)进行修改,修改时要特别小心函数的声明中,有一个[id(n)]的函数序号,可不要乱了;
其三、同步修改H/CPP中的函数声明和函数体;
其四、在CPP文件中,根据情况也要修改BEGIN_DISPATCH_MAP/END_DISPATCH_MAP()函数影射宏。
正确编译后,MFC不象ATL那样会自动注册。
你需要手工执行regsvr32.exe进行注册,或者执行菜单“Tools\Registercontrol”
四、用ATL实现双接口组件(操作方法和步骤,请参考《COM组件设计与应用(五)》)
4-1:
建立一个ATL工程(Project),工程名称为“Simple6”
4-2:
按默认进行。
选择DLL类型、不合并代理和存根代码、不支持MFC、不支持MTS
4-3:
NewAtlObject...选择SimpleObject
4-4:
输入名称和属性,属性按默认进行,也就是dual(双接口)方式(注3)
4-5:
增加函数。
在ClassView卡片中,选择接口、鼠标右键菜单、AddMethod...
Add([in]VARIANTv1,[in]VARIANTv2,[out,retval]VARIANT*pVal);
Upper([in]BSTRstr,[out,retval]BSTR*pVal);
关于Add()函数,你依然可以使用Add([in]longn1,[in]longn2,[out,retval]long*pVal)方式。
但这次我们没有使用long,而是使用了VARIANT做参数和返回值。
这里我先卖个关子,往下看,就知道使用VARIANT的精彩之处了。
4-6:
完成代码
STDMETHODIMPCDispSimple:
:
Add(VARIANTv1,VARIANTv2,VARIANT*pVal)
{
:
:
VariantInit(pVal);//永远初始化返回值是个好习惯
CComVariantv_1(v1);
CComVariantv_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);//转换为字符串
CComBSTRbstr(v_1.bstrVal);
bstr.AppendBSTR(v_2.bstrVal);//字符串连接
pVal->vt=VT_BSTR;
pVal->bstrVal=bstr.Detach();
}
returnS_OK;
}
STDMETHODIMPCDispSimple:
:
Upper(BSTRstr,BSTR*pVal)
{
*pVal=NULL;//永远初始化返回值是个好习惯
CComBSTRs(str);
s.ToUpper();//转换为大写
*pVal=s.Copy();
returnS_OK;
}
刚才卖的关子,现在开始揭密了......加法函数Add()不使用long类型,而使用VARIANT的