IDispatch接口 - GetIDsOfNames和Invoke

来源:互联网 发布:淘宝上买彩票靠谱吗 编辑:程序博客网 时间:2024/05/18 01:11

IDispatch接口是COM自动化的核心。其实,IDispatch这个接口本身也很简单,只有4个方法:

    IDispatch : public IUnknown    {    public:        virtual HRESULT STDMETHODCALLTYPE GetTypeInfoCount(             /* [out] */ __RPC__out UINT *pctinfo) = 0;                virtual HRESULT STDMETHODCALLTYPE GetTypeInfo(             /* [in] */ UINT iTInfo,            /* [in] */ LCID lcid,            /* [out] */ __RPC__deref_out_opt ITypeInfo **ppTInfo) = 0;                virtual HRESULT STDMETHODCALLTYPE GetIDsOfNames(             /* [in] */ __RPC__in REFIID riid,            /* [size_is][in] */ __RPC__in_ecount_full(cNames) LPOLESTR *rgszNames,            /* [range][in] */ __RPC__in_range(0,16384) UINT cNames,            /* [in] */ LCID lcid,            /* [size_is][out] */ __RPC__out_ecount_full(cNames) DISPID *rgDispId) = 0;                virtual /* [local] */ HRESULT STDMETHODCALLTYPE Invoke(             /* [annotation][in] */             _In_  DISPID dispIdMember,            /* [annotation][in] */             _In_  REFIID riid,            /* [annotation][in] */             _In_  LCID lcid,            /* [annotation][in] */             _In_  WORD wFlags,            /* [annotation][out][in] */             _In_  DISPPARAMS *pDispParams,            /* [annotation][out] */             _Out_opt_  VARIANT *pVarResult,            /* [annotation][out] */             _Out_opt_  EXCEPINFO *pExcepInfo,            /* [annotation][out] */             _Out_opt_  UINT *puArgErr) = 0;            };
GetTypeInfoCount和GetTypeInfo以后再说。

先来看看比较熟悉的GetIDsOfNames和Invoke。

GetIDsOfNames

这个函数的主要功能就是:把COM接口的方法名字和参数(可选)映射成一组DISPID。DISPID就是一个LONG型:

typedef LONG DISPID;
GetIDsOfNames()可以获取方法和属性。先来看一个例子,COM接口IMyCar

[object,uuid(21B794E2-4857-4576-8FC2-CDAB2A486600),dual,nonextensible,pointer_default(unique)]interface IMyCar : IDispatch{    [id(1)] HRESULT Run();    [id(2)] HRESULT AddGas([in] LONG add, [out] LONG* total);    [propget, id(3)] HRESULT Gas([out, retval] LONG* pVal);};
这个接口里面有一个方法AddGas,它有两个参数,一个输入,一个输出。它的实现基本如下:

STDMETHODIMP CMyCar::AddGas(LONG add, LONG* total){    // TODO: Add your implementation code here    m_Gas += add;    *total = m_Gas;    return S_OK;}

试试如何获取AddGas函数的id和参数index,看下面的代码。数组里面的第一个元素是方法名字,第二个/第三个是参数名字。

    CComPtr<IMyCar> spCar;    spCar.CoCreateInstance(CLSID_MyCar);

    DISPID PropertyID[3] = {0};    BSTR PropName[3];            PropName[0] = SysAllocString(L"AddGas");    PropName[1] = SysAllocString(L"add");    PropName[2] = SysAllocString(L"total");    HRESULT hr = spCar->GetIDsOfNames(IID_NULL, PropName, 3, LOCALE_SYSTEM_DEFAULT, PropertyID);    SysFreeString(PropName[0]);    SysFreeString(PropName[1]);    SysFreeString(PropName[2]);
运行一下,可以得到如下结果:

PropertyID数组里面可以得到3个值:2, 0, 1.

2代表的是AddGas的id,跟idl文件里面的一样。

0表示"add"是第一个参数,1表示"total"是第二个参数。

Invoke

Invoke是IDispatch里面非常重要的一样函数,方法调用就靠这个函数了。函数原型:

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);

每一个参数的说明,看下面,从MSDN截来的。

先来看一个调用例子:

    CComVariant avarParams[2];    avarParams[1].vt = VT_I4;    avarParams[1] = 4;    LONG vTotal = 0;    avarParams[0].vt = VT_I4 | VT_BYREF;    avarParams[0] = &vTotal;    DISPPARAMS params = { avarParams,        NULL,              // Dispatch identifiers of named arguments.         2,                 // Number of arguments.        0 };                // Number of named arguments.    hr = spCar->Invoke(PropertyID[0], IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &params, NULL, NULL, NULL);
avarParams是一个具有2个元素的数组,它表示要调用的方法的参数,注意这里的参数是逆序的。比如avarParam[1]存放的是第一个参数,是一个输入参数;avarParams[0]存放的是第二个参数,是一个输出参数。

运行一下:

spCar里面的m_Gas初始值是0,我们调用的时候给它加了4,那么输出就应该是4.看上面的运行结果,vTotal确实是4.

这样我们就通过Invoke成功调用了COM组件的方法(而不是通过spCar->AddGas)。注意Invoke里面的第一个参数是一个DISPID,这个是从GetIDsOfNames来的。


使用Invoke也可以调用COM对象的属性。

比如上面的idl里面的Gas。

调用属性跟方法差不多,如果我们想读取一个属性,那么可以这么调:

    DISPID PropertyID2[1] = { 0 };    BSTR PropName2[1];    PropName2[0] = SysAllocString(L"Gas");        hr = spCar->GetIDsOfNames(IID_NULL, PropName2, 1, LOCALE_SYSTEM_DEFAULT, PropertyID2);    SysFreeString(PropName2[0]);    DISPPARAMS params2 = { NULL,        NULL,        0,        0    };        CComVariant Result;    hr = spCar->Invoke(PropertyID2[0], IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, &params2, &Result, NULL, NULL);    

运行可以得到结果:



注意,Invoke的第五个参数啥都没,属性值是从第六个参数返回出来的。如果是方法调用的话,那么COM方法参数需要从第五个参数传进入,第六个参数是函数调用的返回值HRESULT.

我没有试过属性的put,不知道是从哪里传入,当有需要的时候查一下MSDN就行了。


以上就是GetIDsOfNames和Invoke的简要说明以及例子。之后再来分析更多的细节。

完整客户端代码:

// ConsoleApplication4.cpp : Defines the entry point for the console application.//#include "stdafx.h"#include <thread>#include <atlbase.h>#include <atlcom.h>#include <algorithm>#include <vector>#include <memory>#include "../MyCom/MyCom_i.h"#include "../MyCom/MyCom_i.c"int _tmain(int argc, _TCHAR* argv[]){    CoInitializeEx(NULL, COINIT_MULTITHREADED);        CComPtr<IMyCar> spCar;    spCar.CoCreateInstance(CLSID_MyCar);    // use IDispatch    DISPID PropertyID[3] = {0};    BSTR PropName[3];            PropName[0] = SysAllocString(L"AddGas");    PropName[1] = SysAllocString(L"add");    PropName[2] = SysAllocString(L"total");    HRESULT hr = spCar->GetIDsOfNames(IID_NULL, PropName, 3, LOCALE_SYSTEM_DEFAULT, PropertyID);    SysFreeString(PropName[0]);    SysFreeString(PropName[1]);    SysFreeString(PropName[2]);    CComVariant avarParams[2];    avarParams[1].vt = VT_I4;    avarParams[1] = 4;    LONG vTotal = 0;    avarParams[0].vt = VT_I4 | VT_BYREF;    avarParams[0] = &vTotal;    DISPPARAMS params = { avarParams,        NULL,              // Dispatch identifiers of named arguments.         2,                 // Number of arguments.        0 };                // Number of named arguments.    hr = spCar->Invoke(PropertyID[0], IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, ¶ms, NULL, NULL, NULL);        DISPID PropertyID2[1] = { 0 };    BSTR PropName2[1];    PropName2[0] = SysAllocString(L"Gas");        hr = spCar->GetIDsOfNames(IID_NULL, PropName2, 1, LOCALE_SYSTEM_DEFAULT, PropertyID2);    SysFreeString(PropName2[0]);    DISPPARAMS params2 = { NULL,        NULL,        0,        0    };        CComVariant Result;    hr = spCar->Invoke(PropertyID2[0], IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, ¶ms2, &Result, NULL, NULL);        spCar.Release();    CoUninitialize();    return 0;}
相关的COM组件的代码:

STDMETHODIMP CMyCar::AddGas(LONG add, LONG* total){    // TODO: Add your implementation code here    m_Gas += add;    *total = m_Gas;    return S_OK;}STDMETHODIMP CMyCar::get_Gas(LONG* pVal){    // TODO: Add your implementation code here    *pVal = m_Gas;    return S_OK;}






3 0
原创粉丝点击