`
阿尔萨斯
  • 浏览: 4472047 次
社区版块
存档分类
最新评论

IDispatch接口原理与应用

 
阅读更多

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_is][in] */ LPOLESTR *rgszNames,

/* [in] */ UINT cNames,

/* [in] */ LCID lcid,

/* [size_is][out] */ DISPID *rgDispId) = 0;

virtual /* [local] */ HRESULT STDMETHODCALLTYPE Invoke(

/* [in] */ DISPID dispIdMember,

/* [in] */ REFIID riid,

/* [in] */ LCID lcid,

/* [in] */ WORD wFlags,

/* [out][in] */ DISPPARAMS *pDispParams,

/* [out] */ VARIANT *pVarResult,

/* [out] */ EXCEPINFO *pExcepInfo,

/* [out] */ UINT *puArgErr) = 0;

};

我们通过IDispatch的GUID到注册表中搜索,可以搜索到如下结果:

<shapetype id="_x0000_t75" stroked="f" filled="f" path="m@4@5l@4@11@9@11@9@5xe" o:preferrelative="t" o:spt="75" coordsize="21600,21600"><stroke joinstyle="miter"></stroke><formulas><f eqn="if lineDrawn pixelLineWidth 0"></f><f eqn="sum @0 1 0"></f><f eqn="sum 0 0 @1"></f><f eqn="prod @2 1 2"></f><f eqn="prod @3 21600 pixelWidth"></f><f eqn="prod @3 21600 pixelHeight"></f><f eqn="sum @0 0 1"></f><f eqn="prod @6 1 2"></f><f eqn="prod @7 21600 pixelWidth"></f><f eqn="sum @8 21600 0"></f><f eqn="prod @7 21600 pixelHeight"></f><f eqn="sum @10 21600 0"></f></formulas><path o:connecttype="rect" gradientshapeok="t" o:extrusionok="f"></path><lock aspectratio="t" v:ext="edit"></lock></shapetype><shape id="_x0000_i1025" style="WIDTH: 541.5pt; HEIGHT: 303.75pt" type="#_x0000_t75" o:ole=""><imagedata o:title="" src="file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msoclip1/01/clip_image001.png"></imagedata></shape>

注意在IDispatch接口GUID下面还有两个展开的GUID项,他们分别是ITypeInfoITypeLib接口。这两个接口在自动化应用中也是非常重要的。今后我们会经常看到他们。

IDispatch接口方法简介:

1HRESULT 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接口指针不同。如果我们创建的组件根据不同的地域标志对属性和方法起不同的名字,我们就要使用这个参数,否则可以忽略。

3HRESULT STDMETHODCALLTYPE GetIDsOfNames(

/* [in] */ REFIID riid,

/* [size_is][in] */ LPOLESTR *rgszNames,

/* [in] */ UINT cNames,

/* [in] */ LCID lcid,

/* [size_is][out] */ 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,返回该值

#define DISPID_UNKNOWN ( -1 )

/* DISPID reserved for the "value" property */

//如果调用时不指定方法或者属性,则使用该缺省值

#define DISPID_VALUE ( 0 )

/* The following DISPID is reserved to indicate the param

* that is the right-hand-side (or "put" value) of a PropertyPut

*/

//表明属性设置函数中某一个参数将接受新属性值

#define DISPID_PROPERTYPUT ( -3 )

/* DISPID reserved for the standard "NewEnum" method */

//用于集合对象

#define DISPID_NEWENUM ( -4 )

/* DISPID reserved for the standard "Evaluate" method,脚本语言中可以用[]调用该方法 */

#define DISPID_EVALUATE ( -5 )

/* 表示某方法具有和构造函数相同的功能*/

#define DISPID_CONSTRUCTOR ( -6 )

/* 表示某方法具有和析构函数相同的功能*/

#define DISPID_DESTRUCTOR ( -7 )

/* The Collect property. You use this property if the method you are calling through Invoke is an accessor function.*/

#define DISPID_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,

/* [out][in] */ 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。

<<COM原理与应用>>中曾经提到rgvarg数组中的参数存放顺序和客户程序调用时传递的参数顺序刚好相反,我这里对此表示怀疑。

关于命名参数的详细讨论我在后面将谈到,现在只需要知道它可以不受参数次序的限制。

参数pVarResult保存函数调用后的返回信息,因为Invoke已经将返回值用于COM通用的HRESULT;

参数pExcepInfo返回异常信息;

参数puArgErr包含第一个产生错误的参数索引,当Invoke返回的HRESULT值为DISP_E_TYPEMISMATCH或DISP_E_PARAM_NOTFOUND值时。

创建支持IDispatch接口的COM对象:

本节我们利用ATL7.1创建一个COM对象BabyCBaby类从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接口,除了继承了IDispatch4个方法和IUnknown3个方法外,它现在还没有任何自己的方法和属性。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来获取相应的CLSIDProgID是一个友好的名称,用来标志一个组件实现类。通常创建ATL组件后,我们可以在工程中找到组件名.RGS脚本文件,如下面的文件baby.rgs

HKCR //HKEY_CLASSES_ROOT的缩写

{

IDspCOM.Baby.1 = s 'Baby Class'//s代表REG_SZd代表REG_DWORDb代表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 <iostream>

#include <atlstr.h>

#include <string>

using namespace std;

int _tmain(int argc, _TCHAR* argv[])

{

::CoInitialize(NULL);

//创建IDspCOM.Baby对象

CLSID Clsid;

::CLSIDFromProgID(L"IDspCOM.Baby",&Clsid);

IDispatch* pIDsp=NULL;

HRESULT hr=::CoCreateInstance(Clsid,NULL,CLSCTX_ALL,IID_IDispatch,(void**)&pIDsp);

if(FAILED(hr))

{

cout<<"Failed To Create IDspCOM.Babyj Object"<<endl;

::CoUninitialize();

return 0;

}

//检查IDspCOM.Baby对象是否支持类型信息

UINT Count;

pIDsp->GetTypeInfoCount(&Count);

if(Count==0)

{

cout<<"IDspCOM.Babyj Object has not TypeInfo"<<endl;

return 0;

}

//获取属性ID

DISPID PropertyID;

BSTR PropName[1];//BSTR可以在这里作为OLECHAR*使用的前提是我们必须保证BSTR字符串中不内嵌NULL字符

PropName[0]=SysAllocString(L"Gender");

hr=pIDsp->GetIDsOfNames(IID_NULL,PropName,1,LOCALE_SYSTEM_DEFAULT,&PropertyID);

SysFreeString(PropName[0]);

//设置属性Gender值为男

DISPPARAMS Params;

Params.cArgs=1;

Params.cNamedArgs=1;//必须,原因不明

DISPID dispidPut = DISPID_PROPERTYPUT;//必须,原因不明

Params.rgdispidNamedArgs=&dispidPut;//必须,原因不明

Params.rgvarg=new VARIANTARG[1];

Params.rgvarg[0].vt=VT_BSTR;

Params.rgvarg[0].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;

cout<<W2A(Result.bstrVal)<<endl;

//释放接口

pIDsp->Release();

::CoUninitialize();

return 0;

}

采用ATL智能指针类调用IDispatch接口的方法:

采用标准方法调用IDispatch非常繁琐,而且容易出错,为了简化这些过程,ATL7.1类库中提供了一个智能指针类CComDispatchDriver类。定义如下:

typedef CComQIPtr<IDispatch, &__uuidof(IDispatch)> CComDispatchDriver;

实质上它是CComQIPtr类的特化版本。源代码如下:

//specialization for IDispatch

template <>

class CComPtr<IDispatch> : public CComPtrBase<IDispatch>

{

public:

CComPtr() throw()

{

}

CComPtr(IDispatch* lp) throw() :

CComPtrBase<IDispatch>(lp)

{

}

CComPtr(const CComPtr<IDispatch>& lp) throw() :

CComPtrBase<IDispatch>(lp.p)

{

}

IDispatch* operator=(IDispatch* lp) throw()

{

return static_cast<IDispatch*>(AtlComPtrAssign((IUnknown**)&p, lp));

}

IDispatch* operator=(const CComPtr<IDispatch>& lp) throw()

{

return static_cast<IDispatch*>(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<LPOLESTR*>(&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(L"IDspCOM.Baby");

CComVariant Value(L"女");

Dsp.PutPropertyByName(L"Gender",&Value);

CComVariant Value2;

Dsp.GetPropertyByName(L"Gender",&Value2);

::CoUninitialize();

return 0;

}

使用类COleDispatchDriver调用IDispatch的方法:

COleDispatchDriver类是MFC类库中用于操纵IDispatch接口的类。下面是该类的声明:

class COleDispatchDriver

{

// Constructors

public:

COleDispatchDriver();

COleDispatchDriver(LPDISPATCH lpDispatch, BOOL bAutoRelease = TRUE);

COleDispatchDriver(const COleDispatchDriver& dispatchSrc);

// Attributes

LPDISPATCH m_lpDispatch;

BOOL m_bAutoRelease;

// Operations

BOOL CreateDispatch(REFCLSID clsid, COleException* pError = NULL);

BOOL CreateDispatch(LPCTSTR lpszProgID, COleException* pError = NULL);

void AttachDispatch(LPDISPATCH lpDispatch, BOOL bAutoRelease = TRUE);

LPDISPATCH DetachDispatch();

// detach and get ownership of m_lpDispatch

void ReleaseDispatch();

// helpers for IDispatch::Invoke

void AFX_CDECL InvokeHelper(DISPID dwDispID, WORD wFlags,

VARTYPE vtRet, void* pvRet, const BYTE* pbParamInfo, ...);

void AFX_CDECL SetProperty(DISPID dwDispID, VARTYPE vtProp, ...);

void GetProperty(DISPID dwDispID, VARTYPE vtProp, void* pvProp) const;

// special operators

operator LPDISPATCH();

const COleDispatchDriver& operator=(const COleDispatchDriver& dispatchSrc);

// Implementation

public:

~COleDispatchDriver();

void InvokeHelperV(DISPID dwDispID, WORD wFlags, VARTYPE vtRet,

void* pvRet, const BYTE* pbParamInfo, va_list argList);

};

先创建Win32 Console程序,选择支持MFC,然后编写如下代码:

::CoInitialize(NULL);

COleDispatchDriver Dsp;

if(!Dsp.CreateDispatch(L"IDspCOM.Baby"))

{

cout<<L"创建失败"<<endl;

}

DISPID PropID;

BSTR PropName[1];//BSTR可以在这里作为OLECHAR*使用的前提是我们必须保证BSTR字符串中不内嵌NULL字符

PropName[0]=SysAllocString(L"Gender");

Dsp.m_lpDispatch->GetIDsOfNames(IID_NULL,PropName,1,LOCALE_SYSTEM_DEFAULT,&PropID);

Dsp.SetProperty(PropID,VT_BSTR,L"男");

CString Value;

Dsp.GetProperty(PropID,VT_BSTR,&Value);

Dsp.ReleaseDispatch;

::CoUninitialize();

很明显,该类使用比CComDispatchDriver类要繁琐的多,所以不推荐使用。不过MapObjects中的C++类采用派生自该类,同时为了便于使用增加了很多成员函数的技术。

分享到:
评论

相关推荐

    COM 组件设计与应用(十)——IDispatch 接口 for VC.NET源代码

    实现自动化组件. 关键字:IDispatch,automation,自动化

    COM IDispatch接口封装

    在本文中,我们将深入探讨IDispatch接口的原理、用途以及如何进行封装,以帮助开发者更好地理解和应用这一关键技术。 IDispatch接口主要用于提供对对象属性和方法的动态访问,它是COM接口中实现自动化和类型安全的...

    《com原理与应用》高清版的pdf

    2. **接口与IDispatch**:COM的核心之一就是接口,书中会讲解接口的作用和使用方式,特别是对于自动化和晚绑定的支持,如IDispatch接口的使用。 3. **COM对象的生命周期管理**:讲述如何通过引用计数机制来控制对象...

    《COM 原理与应用》例子源代码

    本资源“《COM 原理与应用》例子源代码”包含了深入理解COM概念和实现的实践示例。 在COM中,核心概念是组件(Component),它是一个自包含的、可执行的二进制模块,可以独立于其他组件运行或与其他组件协作。组件...

    《COM原理与应用》源码

    3. **IDispatch接口**:这是COM中用于自动化和晚绑定的主要接口,它允许客户端动态地发现和调用组件的方法。 4. **IUnknown接口**:每个COM对象都必须实现IUnknown接口,它提供了查询接口(QueryInterface)、增加...

    COM原理与应用源代码

    - 实现一个基本的COM服务器,包含IUnknown和IDispatch接口的实现。 - 注册和反注册COM组件。 - 使用 ATL(Active Template Library)或MFC(Microsoft Foundation Classes)库简化COM组件的开发。 - 创建多线程组件...

    《COM原理与应用》全书笔记

    在COM中,类型信息是动态的,这得益于IDispatch接口。然而,为了提高效率,通常会使用IDL(Interface Definition Language)文件来静态定义接口,这样编译器就能在编译阶段生成代理代码,提高运行时性能。 #### 四...

    com原理与应用

    3. IDispatch:这是COM中的一个关键接口,主要用于支持晚绑定和自动化,使得非C++语言可以调用COM组件。 4. CLSID(Class Identifier):每个COM对象都有一个唯一的CLSID,用于识别对象类,通过CLSID可以创建该类的...

    COM 原理与应用.rar

    潘爱民先生的《COM原理与应用》可能深入探讨了COM的核心概念、设计原则以及实际应用。 COM的核心概念包括以下几个方面: 1. **组件**: 在COM中,组件是具有独立功能的软件实体,可以被其他组件或应用程序使用。...

    COM原理及应用源码

    1. COM接口的定义与实现:如何使用IDispatch接口进行晚绑定,以及IUnknown接口的QueryInterface、AddRef和Release方法。 2. 接口继承与多接口组件:如何实现接口的多重继承,以及如何处理多重接口组件的引用计数。 3...

    COM原理与应用例子程序代码

    2. **接口实现**:展示了如何定义和实现COM接口,包括IDispatch接口,用于支持晚绑定和自动化。 3. **事件与连接点**:可能包括COM事件模型的实现,如何使用IConnectionPoint和IConnectionPointContainer接口实现...

    COM 组件设计与应用(九)——IDispatch 接口 for VC6.0源代码

    本资源“COM 组件设计与应用(九)——IDispatch 接口 for VC6.0源代码”专注于讲解如何在Visual C++ 6.0(简称VC6.0)环境下实现自动化组件,并利用IDispatch接口进行通信。IDispatch接口是COM(Component Object ...

    [COM原理与应用]配套源码 (完整源码)

    本资源是与《COM原理与应用》这本书相关的配套源码,提供了多个章节的示例代码,帮助读者深入理解COM技术。 源码目录结构包括了多个章节的代码,如"ch12"、"ch4"、"ch5"、"ch7"、"ch11"、"ch6"、"ch2"等,这些章节...

    【COM原理与应用pdf 】及源码

    IDispatch接口用于支持晚绑定,使得不熟悉对象具体类型的客户端也能调用其方法。 3. **COM注册与撤销注册**:解释如何通过注册表来管理COM组件,以及如何在系统中添加、删除和查找COM对象。 4. **IUnknown接口**:...

    COM原理与应用

    3. **IDispatch接口**:除了IUnknown,还有一种常用接口IDispatch,主要用于自动化和脚本环境,提供晚绑定(late binding)的能力,允许在运行时动态获取接口和方法。 4. **组件注册**:COM组件在安装时需要注册到...

    《com原理与应用》示例代码\源代码

    在《COM原理与应用》一书中,作者深入浅出地讲解了COM的基本概念、设计原则以及如何创建和使用COM组件。这本书的示例代码是为了配合理论部分,帮助读者更好地理解和实践COM技术。遗憾的是,书中的链接已经失效,无法...

    《com原理与应用》源代码

    4. **自动化接口**:如IDispatch接口,用于支持晚绑定和脚本语言与COM组件的交互,通常用于自动化和Scripting。 5. **注册和卸载**:源代码会演示如何注册COM组件到系统注册表,以便其他程序可以定位并使用它们,...

    潘爱民_com原理与应用( 源码)

    "潘爱民_com原理与应用(源码)"很可能是潘爱民先生关于COM技术的著作,其中包含了书中讲解的各种示例源代码。 在COM中,每个组件都是一个独立的二进制实体,具有自己的接口,允许其他组件或应用程序与之交互。COM的...

Global site tag (gtag.js) - Google Analytics