IDispatch接口 - Part I - GetIDsOfNames和Invoke

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

  1. IDispatch : public IUnknown 
  2. public
  3.     virtual HRESULT STDMETHODCALLTYPE GetTypeInfoCount(  
  4.         /* [out] */ __RPC__out UINT *pctinfo) = 0; 
  5.      
  6.     virtual HRESULT STDMETHODCALLTYPE GetTypeInfo(  
  7.         /* [in] */ UINT iTInfo, 
  8.         /* [in] */ LCID lcid, 
  9.         /* [out] */ __RPC__deref_out_opt ITypeInfo **ppTInfo) = 0; 
  10.      
  11.     virtual HRESULT STDMETHODCALLTYPE GetIDsOfNames(  
  12.         /* [in] */ __RPC__in REFIID riid, 
  13.         /* [size_is][in] */ __RPC__in_ecount_full(cNames) LPOLESTR *rgszNames,
  14.         /* [range][in] */ __RPC__in_range(0,16384) UINT cNames, 
  15.         /* [in] */ LCID lcid, 
  16.         /* [size_is][out] */ __RPC__out_ecount_full(cNames) DISPID *rgDispId) = 0; 
  17.      
  18.     virtual /* [local] */ HRESULT STDMETHODCALLTYPE Invoke(  
  19.         /* [annotation][in] */  
  20.         _In_  DISPID dispIdMember, 
  21.         /* [annotation][in] */  
  22.         _In_  REFIID riid, 
  23.         /* [annotation][in] */  
  24.         _In_  LCID lcid, 
  25.         /* [annotation][in] */  
  26.         _In_  WORD wFlags, 
  27.         /* [annotation][out][in] */  
  28.         _In_  DISPPARAMS *pDispParams, 
  29.         /* [annotation][out] */  
  30.         _Out_opt_  VARIANT *pVarResult, 
  31.         /* [annotation][out] */  
  32.         _Out_opt_  EXCEPINFO *pExcepInfo, 
  33.         /* [annotation][out] */  
  34.         _Out_opt_  UINT *puArgErr) = 0; 
  35.      
  36. }; 
    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型:

  1. typedef LONG DISPID; 
typedef LONG DISPID;

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

  1.     object, 
  2.     uuid(21B794E2-4857-4576-8FC2-CDAB2A486600), 
  3.     dual, 
  4.     nonextensible, 
  5.     pointer_default(unique) 
  6. interface IMyCar : IDispatch{ 
  7.     [id(1)] HRESULT Run(); 
  8.     [id(2)] HRESULT AddGas([in] LONG add, [out] LONG* total); 
  9.     [propget, id(3)] HRESULT Gas([out, retval] LONG* pVal); 
  10. }; 
[
	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,它有两个参数,一个输入,一个输出。它的实现基本如下:

  1. STDMETHODIMP CMyCar::AddGas(LONG add, LONG* total) 
  2.     // TODO: Add your implementation code here 
  3.     m_Gas += add; 
  4.     *total = m_Gas; 
  5.  
  6.     return S_OK; 
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,看下面的代码。数组里面的第一个元素是方法名字,第二个/第三个是参数名字。

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

  1. DISPID PropertyID[3] = {0}; 
  2. BSTR PropName[3]; 
  3.      
  4. PropName[0] = SysAllocString(L"AddGas"); 
  5. PropName[1] = SysAllocString(L"add"); 
  6. PropName[2] = SysAllocString(L"total"); 
  7. HRESULT hr = spCar->GetIDsOfNames(IID_NULL, PropName, 3, LOCALE_SYSTEM_DEFAULT, PropertyID); 
  8.  
  9. SysFreeString(PropName[0]); 
  10. SysFreeString(PropName[1]); 
  11. SysFreeString(PropName[2]); 
    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里面非常重要的一样函数,方法调用就靠这个函数了。函数原型:

  1. HRESULT Invoke( 
  2.   [in]       DISPID dispIdMember, 
  3.   [in]       REFIID riid, 
  4.   [in]       LCID lcid, 
  5.   [in]       WORD wFlags, 
  6.   [in, out]  DISPPARAMS *pDispParams, 
  7.   [out]      VARIANT *pVarResult, 
  8.   [out]      EXCEPINFO *pExcepInfo, 
  9.   [out]      UINT *puArgErr 
  10. ); 
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截来的。

先来看一个调用例子:

  1. CComVariant avarParams[2]; 
  2. avarParams[1].vt = VT_I4; 
  3. avarParams[1] = 4; 
  4.  
  5. LONG vTotal = 0; 
  6. avarParams[0].vt = VT_I4 | VT_BYREF; 
  7. avarParams[0] = &vTotal; 
  8.  
  9. DISPPARAMS params = { avarParams, 
  10.     NULL,              // Dispatch identifiers of named arguments.  
  11.     2,                 // Number of arguments. 
  12.     0 };                // Number of named arguments. 
  13.  
  14. hr = spCar->Invoke(PropertyID[0], IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &params, NULL, NULL, NULL); 
    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。

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

  1. DISPID PropertyID2[1] = { 0 }; 
  2. BSTR PropName2[1]; 
  3.  
  4. PropName2[0] = SysAllocString(L"Gas"); 
  5.  
  6. hr = spCar->GetIDsOfNames(IID_NULL, PropName2, 1, LOCALE_SYSTEM_DEFAULT, PropertyID2); 
  7.  
  8. SysFreeString(PropName2[0]); 
  9.  
  10.  
  11. DISPPARAMS params2 = { NULL, 
  12.     NULL, 
  13.     0, 
  14.     0 
  15. }; 
  16.  
  17. CComVariant Result; 
  18. hr = spCar->Invoke(PropertyID2[0], IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, &params2, &Result, 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, &params2, &Result, NULL, NULL);
    


运行可以得到结果:



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

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


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

完整客户端代码:

  1. // ConsoleApplication4.cpp : Defines the entry point for the console application. 
  2. // 
  3.  
  4. #include "stdafx.h" 
  5.  
  6. #include <thread> 
  7. #include <atlbase.h> 
  8. #include <atlcom.h> 
  9. #include <algorithm> 
  10. #include <vector> 
  11. #include <memory> 
  12.  
  13. #include "../MyCom/MyCom_i.h" 
  14. #include "../MyCom/MyCom_i.c" 
  15.  
  16. int _tmain(int argc, _TCHAR* argv[]) 
  17.     CoInitializeEx(NULL, COINIT_MULTITHREADED); 
  18.      
  19.     CComPtr<IMyCar> spCar; 
  20.     spCar.CoCreateInstance(CLSID_MyCar); 
  21.  
  22.     // use IDispatch 
  23.     DISPID PropertyID[3] = {0}; 
  24.     BSTR PropName[3]; 
  25.          
  26.     PropName[0] = SysAllocString(L"AddGas"); 
  27.     PropName[1] = SysAllocString(L"add"); 
  28.     PropName[2] = SysAllocString(L"total"); 
  29.     HRESULT hr = spCar->GetIDsOfNames(IID_NULL, PropName, 3, LOCALE_SYSTEM_DEFAULT, PropertyID); 
  30.  
  31.     SysFreeString(PropName[0]); 
  32.     SysFreeString(PropName[1]); 
  33.     SysFreeString(PropName[2]); 
  34.  
  35.     CComVariant avarParams[2]; 
  36.     avarParams[1].vt = VT_I4; 
  37.     avarParams[1] = 4; 
  38.  
  39.     LONG vTotal = 0; 
  40.     avarParams[0].vt = VT_I4 | VT_BYREF; 
  41.     avarParams[0] = &vTotal; 
  42.  
  43.     DISPPARAMS params = { avarParams, 
  44.         NULL,              // Dispatch identifiers of named arguments.  
  45.         2,                 // Number of arguments. 
  46.         0 };                // Number of named arguments. 
  47.  
  48.     hr = spCar->Invoke(PropertyID[0], IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, ¶ms, NULL, NULL, NULL); 
  49.      
  50.  
  51.     DISPID PropertyID2[1] = { 0 }; 
  52.     BSTR PropName2[1]; 
  53.  
  54.     PropName2[0] = SysAllocString(L"Gas"); 
  55.      
  56.     hr = spCar->GetIDsOfNames(IID_NULL, PropName2, 1, LOCALE_SYSTEM_DEFAULT, PropertyID2); 
  57.  
  58.     SysFreeString(PropName2[0]); 
  59.  
  60.  
  61.     DISPPARAMS params2 = { NULL, 
  62.         NULL, 
  63.         0, 
  64.         0 
  65.     }; 
  66.      
  67.     CComVariant Result; 
  68.     hr = spCar->Invoke(PropertyID2[0], IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, ¶ms2, &Result, NULL, NULL); 
  69.      
  70.     spCar.Release(); 
  71.  
  72.     CoUninitialize(); 
  73.      
  74.     return 0; 
// 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组件的代码:

  1. STDMETHODIMP CMyCar::AddGas(LONG add, LONG* total) 
  2.     // TODO: Add your implementation code here 
  3.     m_Gas += add; 
  4.     *total = m_Gas; 
  5.  
  6.     return S_OK; 
  7.  
  8.  
  9. STDMETHODIMP CMyCar::get_Gas(LONG* pVal) 
  10.     // TODO: Add your implementation code here 
  11.     *pVal = m_Gas; 
  12.  
  13.     return S_OK; 
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;
}





 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值