




已阅读5页,还剩14页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
IDispatch接口原理与应用目录:IDispatch接口原理与应用1 目录:1 前言:1 IDispatch接口的定义:1 创建支持IDispatch接口的COM对象:4 标准方式调用IDispatch接口的方法:5 采用ATL智能指针类调用IDispatch接口的方法:7 使用类COleDispatchDriver调用IDispatch的方法:11 前言:尽管com接口是跨语言的,但是很多语言在使用com时更多地通过Automation技术来和com对象通信。IDispatch接口是Automation的核心技术。尽管c+程序员并不喜欢甚至讨厌使用IDispatch接口,因为调用它实在是非常的麻烦而且易出错。但是不可否认大量的现存组件是只基于IDispatch接口技术而开发的,有时候你没有选择,而且如果你想要写一些组件能够在web上运行,你也离不开IDisptch接口,因为VBScript这样的脚本语言不会聪明到能够理解你的基于虚函数表的普通com接口。与其躲避它,不如征服它。本文中,我将结合自己的经验和读者一起探讨IDispatch接口的各种应用。并介绍几种能够加快我们使用IDispatch接口的c+类。 IDispatch接口的定义:参照文件oaidl.h中的定义-MIDL_INTERFACE(00020400-0000-0000-C000-000000000046) IDispatch : public IUnknown public: virtual HRESULT STDMETHODCALLTYPE GetTypeInfoCount( /* out */ UINT *pctinfo) = 0; virtual HRESULT STDMETHODCALLTYPE GetTypeInfo( /* in */ UINT iTInfo, /* in */ LCID lcid, /* out */ ITypeInfo *ppTInfo) = 0; virtual HRESULT STDMETHODCALLTYPE GetIDsOfNames( /* in */ REFIID riid, /* size_isin */ LPOLESTR *rgszNames, /* in */ UINT cNames, /* in */ LCID lcid, /* size_isout */ DISPID *rgDispId) = 0; virtual /* local */ HRESULT STDMETHODCALLTYPE Invoke( /* in */ DISPID dispIdMember, /* in */ REFIID riid, /* in */ LCID lcid, /* in */ WORD wFlags, /* outin */ DISPPARAMS *pDispParams, /* out */ VARIANT *pVarResult, /* out */ EXCEPINFO *pExcepInfo, /* out */ UINT *puArgErr) = 0; ; 我们通过IDispatch的GUID到注册表中搜索,可以搜索到如下结果: 注意在IDispatch接口GUID下面还有两个展开的GUID项,他们分别是ITypeInfo和ITypeLib接口。这两个接口在自动化应用中也是非常重要的。今后我们会经常看到他们。IDispatch接口方法简介:1)HRESULT STDMETHODCALLTYPE GetTypeInfoCount( /* out */ UINT *pctinfo) ; 判断实现了IDispatch接口的对象是否支持类型信息,如果返回1则支持,返回0则不支持。 2) HRESULT STDMETHODCALLTYPE GetTypeInfo( /* in */ UINT iTInfo, /* in */ LCID lcid, /* out */ ITypeInfo *ppTInfo) = 0; 获取对象的类型信息接口指针,该方法调用之前总应该先调用方法GetTypeInfoCount()确认是否支持类型信息。 参数iTInfo必须为0,否则该方法将返回DISP_E_BADINDEX表示失败参数lcid传递类型信息的地域标志。IDispatch接口的方法和属性在不同的语言环境(地域标志)可以使用不同的名称,因而lcid不同可能会导致返回的ITypeInfo接口指针不同。如果我们创建的组件根据不同的地域标志对属性和方法起不同的名字,我们就要使用这个参数,否则可以忽略。3)HRESULT STDMETHODCALLTYPE GetIDsOfNames( /* in */ REFIID riid, /* size_isin */ LPOLESTR *rgszNames, /* in */ UINT cNames, /* in */ LCID lcid, /* size_isout */ DISPID *rgDispId) IDispatch接口的属性实质上是方法,方法也就是成员函数,IDispatch接口把所有成员函数的入口地址放入到一个数组中,并且内部组织了一个Map,将数组索引和方法名称一一映射。我们常见的DISPID就是这些方法在数组中的索引。如果我们想调用某一个方法,我们就需要DISPID来让我们找到该方法的地址。 参数riid必须为NULL。 参数rgszNames为字符串数组,第一个字符串为方法或者属性的名称,后续的字符串为参数名称,IDispatch接口的参数也可以有名字。 参数cNames指定rgszNames数组中字符串的个数。 参数lcid传递地域标志,同GetTypeInfo方法中的参数。 参数rgDispId输出一个数组,每个数组成员对应rgszNames中的一个字符串名称。 关于DISPID的进一步说明: typedef LONG DISPID; typedef DISPID MEMBERID; DISPID小于等于0的值都是有特殊意义的,如下面介绍的- /* DISPID reserved to indicate an unknown name */ /* only reserved for data members (properties); reused as a method dispid below */ /如果GetIDsOfNames函数找不到与名称相对应的DISPID,返回该值 #defineDISPID_UNKNOWN( -1 ) /* DISPID reserved for the value property */ /如果调用时不指定方法或者属性,则使用该缺省值 #defineDISPID_VALUE( 0 ) /* The following DISPID is reserved to indicate the param * that is the right-hand-side (or put value) of a PropertyPut */ /表明属性设置函数中某一个参数将接受新属性值 #defineDISPID_PROPERTYPUT( -3 ) /* DISPID reserved for the standard NewEnum method */ /用于集合对象 #defineDISPID_NEWENUM( -4 ) /* DISPID reserved for the standard Evaluate method,脚本语言中可以用调用该方法 */ #defineDISPID_EVALUATE( -5 ) /* 表示某方法具有和构造函数相同的功能*/ #defineDISPID_CONSTRUCTOR( -6 ) /* 表示某方法具有和析构函数相同的功能*/ #defineDISPID_DESTRUCTOR( -7 ) /* The Collect property. You use this property if the method you are calling through Invoke is an accessor function.*/ #defineDISPID_COLLECT( -8 ) /* The range -500 through -999 is reserved for Controls */ /* The range 0x80010000 through 0x8001FFFF is reserved for Controls */ /* The range -5000 through -5499 is reserved for ActiveX Accessability */ /* The range -2000 through -2499 is reserved for VB5 */ /* The range -3900 through -3999 is reserved for Forms */ /* The range -5500 through -5550 is reserved for Forms */ /* The remainder of the negative DISPIDs are reserved for future use */ 4) HRESULT STDMETHODCALLTYPE Invoke( /* in */ DISPID dispIdMember, /* in */ REFIID riid, /* in */ LCID lcid, /* in */ WORD wFlags, /* outin */ DISPPARAMS *pDispParams, /* out */ VARIANT *pVarResult, /* out */ EXCEPINFO *pExcepInfo, /* out */ UINT *puArgErr) 参数dispIdMember为方法或者属性的DISPID,就是我们通过GetIDsOfNames获得的。 参数riid必须为IID_NULL。 参数lcid为地域标志,同前面两个方法。 参数wFlags有下面若干值- Value Description DISPATCH_METHOD 表示将调用方法。如果属性名称和方法名称相同,则和DISPATCH_PROPERTYGET标志一起设置。 DISPATCH_PROPERTYGET 获得属性 DISPATCH_PROPERTYPUT 设置属性 DISPATCH_PROPERTYPUTREF 通过引用设置属性 参数pDispParams为参数信息数组,元素类型为DISPPARAMS typedef struct tagDISPPARAMS /* size_is */ VARIANTARG *rgvarg;/参数数组 /* size_is */ DISPID *rgdispidNamedArgs;/参数中的DISPID数组 UINT cArgs;/数组中的参数个数 UINT cNamedArgs;/命名参数的个数 DISPPARAMS; 注意: 如果是属性设置,rgvarg数组中只有一个参数,如果是方法调用,可以包含0到多个参数; rgvarg数组中的VARIANTARG参数的vt域为VT_BYREF时,该参数可写,否则为只读; rgvarg数组中的VARIANTARG参数的vt域为VT_BYREF时,该参数可以作为输出参数; rgvarg数组中的VARIANTARG参数的vt域不为VT_BYREF时,参数内的字符串或者指针变量的所有权在客户,客户必须自己释放资源,实现IDispatch接口的对象要想保留数据,则要拷贝数据或者调用指针变量的AddRef函数。 rgvarg数组中的VARIANTARG参数的vt域为VT_ERROR,并且scode域为DISP_E_PARAMNOTFOUND时,该参数可以作为可选参数,scode域作用是存放返回的HRESULT。 中曾经提到rgvarg数组中的参数存放顺序和客户程序调用时传递的参数顺序刚好相反,我这里对此表示怀疑。 关于命名参数的详细讨论我在后面将谈到,现在只需要知道它可以不受参数次序的限制。 参数pVarResult保存函数调用后的返回信息,因为Invoke已经将返回值用于COM通用的HRESULT; 参数pExcepInfo返回异常信息; 参数puArgErr包含第一个产生错误的参数索引,当Invoke返回的HRESULT值为DISP_E_TYPEMISMATCH或DISP_E_PARAM_NOTFOUND值时。 创建支持IDispatch接口的COM对象:本节我们利用ATL7.1创建一个COM对象Baby。CBaby类从IDispatch接口派生,并且同时支持vtable方式。我们设想他有一个属性Gender (性别),我们可以设置和获取宝宝的性别属性 现在我们先创建一个ATL项目IDspCOM。然后添加类CBaby,选择接口类型为双重。我们先在看一下生成的idl文件中的接口定义: object, uuid(22C1BD80-2937-42FB-A7F8-5CEBD1257CB8), dual, nonextensible, helpstring(IBaby 接口), pointer_default(unique) interface IBaby : IDispatch ;现在IBaby派生自IDispatch接口,除了继承了IDispatch的4个方法和IUnknown的3个方法外,它现在还没有任何自己的方法和属性。ATL将帮我们实现前面的7个方法,我们无需关心。我们现在来创建自己的属性Gender。我们通过向导创建了该属性,idl文件如下: interface IBaby : IDispatch propget, id(1), helpstring(属性 Gender) HRESULT Gender(out, retval BSTR* pVal); propput, id(1), helpstring(属性 Gender) HRESULT Gender(in BSTR newVal); ;我们可以看到其实属性就是一对方法-设置和获取属性方法。两个方法共用一个DISPID,值为1。在类的头文件中我们添加如下代码:public: STDMETHOD(get_Gender)(BSTR* pVal); STDMETHOD(put_Gender)(BSTR newVal); private: CComBSTR m_Gender; 在类的实现文件中我们添加如下代码: STDMETHODIMP CBaby:get_Gender(BSTR* pVal) m_Gender.CopyTo(pVal); return S_OK; STDMETHODIMP CBaby:put_Gender(BSTR newVal) m_Gender=newVal; return S_OK; 标准方式调用IDispatch接口的方法: 好了,我们现在来编写客户程序创建CBaby组件的实例并设置和获取属性。通常我们编写c+程序调用com时需要导入组件dll文件或者tlb文件。但是由于IDispatch接口提供了统一的方式访问,所以我们可以不必非要这些文件。现在我们创建Win32 Console程序。我们需要CBaby类的CLSID才能够创建该对象,我们可以通过ProgID来获取相应的CLSID,ProgID是一个友好的名称,用来标志一个组件实现类。通常创建ATL组件后,我们可以在工程中找到组件名.RGS脚本文件,如下面的文件baby.rgs:HKCR /HKEY_CLASSES_ROOT的缩写 IDspCOM.Baby.1 = s Baby Class/s代表REG_SZ;d代表REG_DWORD;b代表REG_BINARY CLSID = s 79278E86-6551-40EB-9BB0-25655A1EE60D IDspCOM.Baby = s Baby Class CLSID = s 79278E86-6551-40EB-9BB0-25655A1EE60D CurVer = s IDspCOM.Baby.1 NoRemove CLSID /注销组件时不能删除CLSID关键字 ForceRemove 79278E86-6551-40EB-9BB0-25655A1EE60D = s Baby Class /写该键时应该删除当前键和所有子健 ProgID = s IDspCOM.Baby.1/有版本号的ProgID VersionIndependentProgID = s IDspCOM.Baby/无版本号的ProgID ForceRemove Programmable InprocServer32 = s %MODULE% val ThreadingModel = s Both val AppID = s %APPID% TypeLib = s 5B0732AF-E621-4E5A-A3EE-7F543CFB6701 WIN32 CONSOLE程序可以代码如下:(使用Unicode编码)/ IDspTest.cpp : 定义控制台应用程序的入口点。 / #include stdafx.h #include #include #include using namespace std; int _tmain(int argc, _TCHAR* argv) :CoInitialize(NULL); /创建IDspCOM.Baby对象 CLSID Clsid; :CLSIDFromProgID(LIDspCOM.Baby,&Clsid); IDispatch* pIDsp=NULL; HRESULT hr=:CoCreateInstance(Clsid,NULL,CLSCTX_ALL,IID_IDispatch,(void*)&pIDsp); if(FAILED(hr) coutFailed To Create IDspCOM.Babyj ObjectGetTypeInfoCount(&Count); if(Count=0) coutIDspCOM.Babyj Object has not TypeInfoGetIDsOfNames(IID_NULL,PropName,1,LOCALE_SYSTEM_DEFAULT,&PropertyID); SysFreeString(PropName0); /设置属性Gender值为男 DISPPARAMS Params; Params.cArgs=1; Params.cNamedArgs=1;/必须,原因不明 DISPID dispidPut = DISPID_PROPERTYPUT;/必须,原因不明 Params.rgdispidNamedArgs=&dispidPut;/必须,原因不明 Params.rgvarg=new VARIANTARG1; Params.rgvarg0.vt=VT_BSTR; Params.rgvarg0.bstrVal=SysAllocString(L男); CComVariant Result; EXCEPINFO Info; UINT ArgErr; hr=pIDsp-Invoke(PropertyID,IID_NULL,GetUserDefaultLCID(),DISPATCH_PROPERTYPUT,&Params,&Result,&Info,&ArgErr); VariantClear(Params.rgvarg); delete Params.rgvarg; /获取属性Gender值 DISPPARAMS dispparamsNoArgs = NULL, NULL, 0, 0; hr=pIDsp-Invoke(PropertyID,IID_NULL,GetUserDefaultLCID(),DISPATCH_PROPERTYGET,&dispparamsNoArgs,&Result,&Info,&ArgErr); USES_CONVERSION; coutW2A(Result.bstrVal)Release(); :CoUninitialize(); return 0; 采用ATL智能指针类调用IDispatch接口的方法:采用标准方法调用IDispatch非常繁琐,而且容易出错,为了简化这些过程,ATL7.1类库中提供了一个智能指针类CComDispatchDriver类。定义如下:typedef CComQIPtr CComDispatchDriver;实质上它是CComQIPtr类的特化版本。源代码如下:/specialization for IDispatch template class CComPtr : public CComPtrBase public: CComPtr() throw() CComPtr(IDispatch* lp) throw() : CComPtrBase(lp) CComPtr(const CComPtr& lp) throw() : CComPtrBase(lp.p) IDispatch* operator=(IDispatch* lp) throw() return static_cast(AtlComPtrAssign(IUnknown*)&p, lp); IDispatch* operator=(const CComPtr& lp) throw() return static_cast(AtlComPtrAssign(IUnknown*)&p, lp.p); / IDispatch specific stuff HRESULT GetPropertyByName(LPCOLESTR lpsz, VARIANT* pVar) throw() ATLASSERT(p); ATLASSERT(pVar); DISPID dwDispID; HRESULT hr = GetIDOfName(lpsz, &dwDispID); if (SUCCEEDED(hr) hr = GetProperty(dwDispID, pVar); return hr; HRESULT GetProperty(DISPID dwDispID, VARIANT* pVar) throw() return GetProperty(p, dwDispID, pVar); HRESULT PutPropertyByName(LPCOLESTR lpsz, VARIANT* pVar) throw() ATLASSERT(p); ATLASSERT(pVar); DISPID dwDispID; HRESULT hr = GetIDOfName(lpsz, &dwDispID); if (SUCCEEDED(hr) hr = PutProperty(dwDispID, pVar); return hr; HRESULT PutProperty(DISPID dwDispID, VARIANT* pVar) throw() return PutProperty(p, dwDispID, pVar); HRESULT GetIDOfName(LPCOLESTR lpsz, DISPID* pdispid) throw() return p-GetIDsOfNames(IID_NULL, const_cast(&lpsz), 1, LOCALE_USER_DEFAULT, pdispid); / Invoke a method by DISPID with no parameters HRESULT Invoke0(DISPID dispid, VARIANT* pvarRet = NULL) throw() DISPPARAMS dispparams = NULL, NULL, 0, 0; return p-Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &dispparams, pvarRet, NULL, NULL); / Invoke a method by name with no parameters HRESULT Invoke0(LPCOLESTR lpszName, VARIANT* pvarRet = NULL) throw() HRESULT hr; DISPID dispid; hr = GetIDOfName(lpszName, &dispid); if (SUCCEEDED(hr) hr = Invoke0(dispid, pvarRet); return hr; / Invoke a method by DISPID with a single parameter HRESULT Invoke1(DISPID dispid, VARIANT* pvarParam1, VARIANT* pvarRet = NULL) throw() DISPPARAMS dispparams = pvarParam1, NULL, 1, 0; return p-Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &dispparams, pvarRet, NULL, NULL); / Invoke a method by name with a single parameter HRESULT Invoke1(LPCOLESTR lpszName, VARIANT* pvarParam1, VARIANT* pvarRet = NULL) throw() HRESULT hr; DISPID dispid; hr = GetIDOfName(lpszName, &dispid); if (SUCCEEDED(hr) hr = Invoke1(dispid, pvarParam1, pvarRet); return hr; / Invoke a method by DISPID with two parameters HRESULT Invoke2(DISPID dispid, VARIANT* pvarParam1, VARIANT* pvarParam2, VARIANT* pvarRet = NULL) throw(); / Invoke a method by name with two parameters HRESULT Invoke2(LPCOLESTR lpszName, VARIANT* pvarParam1, VARIANT* pvarParam2, VARIANT* pvarRet = NULL) throw() HRESULT hr; DISPID dispid; hr = GetIDOfName(lpszName, &dispid); if (SUCCEEDED(hr) hr = Invoke2(dispid, pvarParam1, pvarParam2, pvarRet); return hr; / Invoke a method by DISPID with N parameters HRESULT InvokeN(DISPID dispid, VARIANT* pvarParams, int nParams, VARIANT* pvarRet = NULL) throw() DISPPARAMS dispparams = pvarParams, NULL, nParams, 0; return p-Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &dispparams, pvarRet, NULL, NULL); / Invoke a method by name with Nparameters HRESULT InvokeN(LPCOLESTR lpszName, VARIANT* pvarParams, int nParams, VARIANT* pvarRet = NULL) throw() HRESULT hr; DISPID dispid; hr = GetIDOfName(lpszName, &dispid); if (SUCCEEDED(hr) hr = InvokeN(dispid, pvarParams, nParams, pvarRet); return hr; static HRESULT PutProperty(IDispatch* p, DISPID dwDispID, VARIANT* pVar) throw() ATLASSERT(p); ATLASSERT(pVar != NULL); if (pVar = NULL) return E_POINTER; if(p = NULL) return E_INVALIDARG; ATLTRACE(atlTraceCOM, 2, _T(CPropertyHelper:PutProperty/n); DISPPARAMS dispparams = NULL, NULL, 1, 1; dispparams.rgvarg = pVar; DISPID dispidPut = DISPID_PROPERTYPUT; dispparams.rgdispidNamedArgs = &dispidPut; if (pVar-vt = VT_UNKNOWN | pVar-vt = VT_DISPATCH | (pVar-vt & VT_ARRAY) | (pVar-vt & VT_BYREF) HRESULT hr = p-Invoke(dwDispID, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYPUTREF, &dispparams, NULL, NULL, NULL); if (SUCCEEDED(hr) return hr; return p-Invoke(dwDispID, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYPUT, &dispparams, NULL, NULL, NULL); static HRESULT GetProperty(IDispatch* p, DISPID dwDispID, VARIANT* pVar) throw() ATLASSERT(p); ATLASSERT(pVar != NULL); if (pVar = NULL) return E_POINTER; if(p = NULL) return E_INVALIDARG; ATLTRACE(atlTraceCOM, 2, _T(CPropertyHelper:GetProperty/n); DISPPARAMS dispparamsNoArgs = NULL, NULL, 0, 0; return p-Invoke(dwDispID, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, &dispparamsNoArgs, pVar, NULL, NULL); ; 下面我们的客户程序将使用该类完成前一节的属性设置和获取功能:int _tmain(int argc, _TCHAR* argv) :CoInitialize(NULL); CComDispatchDriver Dsp; Dsp.CoCreateInstance(LIDspCOM.Baby); CComVariant Value(L女); Dsp.PutPropertyByName(LGender,&Value); CComVariant Value2; Dsp.GetPropertyByName(LGender,&Value2); :CoUninitialize(); return 0; 使用类COleDispatchDriver调用IDispatch的方法: COleDispatchDriver类是MFC类库中用于操纵IDispatch接口的类。下面是该类的声明:class COleDispatchDriver / Constructors publ
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 废旧锂电池综合回收利用项目技术方案
- 2025年天文知识竞赛试题及答案
- 房屋建筑结构连接技术方案
- 人工智能算力中心项目建筑工程方案
- 仓储方案咨询工作内容
- 培养人工智能人才的策略及实施路径
- 海洋牧场的可持续发展策略探讨
- 电石尾气综合利用项目可行性研究报告
- 月子会所网络营销方案
- 战略定位培训咨询方案
- 2025年中外合资企业合作框架协议模板
- 《餐饮点菜》课件
- 公司财务知到智慧树章节测试课后答案2024年秋北京第二外国语学院
- 2025年天马微电子股份有限公司招聘笔试参考题库含答案解析
- 2025年浙江事业单位联考真题解析历年高频重点提升(共500题)附带答案详解
- 防止人身伤亡事故培训课件(共139张)
- 宣传物料技术服务方案设计
- 检验科质量管理手册
- 幼儿园讲解海军知识
- 《中国货币发展史》课件
- 暴聋(突发性耳聋)中医临床路径及入院标准2020版
评论
0/150
提交评论