轉自: http://blog.csdn.net/csfreebird/article/details/463031
盡管c++程序員並不喜歡甚至討厭使用IDispatch接口,因爲調用它實在是非常的麻煩而且易出錯。但是不可否認大量的現存組件是隻基於IDispatch接口技術而開發的,有時候你沒有選擇,而且如果你想要寫一些組件能夠在web上運行,你也離不開IDisptch接口,因爲VBScript這樣的腳本語言不會聰明到能夠理解你的基於虛函數表的普通com接口。
與其躲避它,不如征服它。本文中,我將結合自己的經驗和讀者一起探討IDispatch接口的各種應用。並介紹幾種能夠加快我們使用IDispatch接口的c++類。
接口的定義:
參照文件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到註冊表中搜索,可以搜索到如下結果:
注意在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_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對象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 <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++類採用派生自該類,同時爲了便於使用增加了很多成員函數的技術。