zoukankan      html  css  js  c++  java
  • IDispatch接口

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

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

    GetTypeInfoCount和GetTypeInfo以后再说。

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

    GetIDsOfNames

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

    1 typedef LONG DISPID;

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

     1 [
     2     object,
     3     uuid(21B794E2-4857-4576-8FC2-CDAB2A486600),
     4     dual,
     5     nonextensible,
     6     pointer_default(unique)
     7 ]
     8 interface IMyCar : IDispatch{
     9     [id(1)] HRESULT Run();
    10     [id(2)] HRESULT AddGas([in] LONG add, [out] LONG* total);
    11     [propget, id(3)] HRESULT Gas([out, retval] LONG* pVal);
    12 };

    这个接口里面有一个方法AddGas,它有两个参数,一个输入,一个输出。它的实现基本如下:

    1 STDMETHODIMP CMyCar::AddGas(LONG add, LONG* total)
    2 {
    3     // TODO: Add your implementation code here
    4     m_Gas += add;
    5     *total = m_Gas;
    6 
    7     return S_OK;
    8 }

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

    1     CComPtr<IMyCar> spCar;
    2     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]);

    运行一下,可以得到如下结果:

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

    每一个参数的说明,看下面,从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);

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

    运行可以得到结果:

    注意,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 {
    18     CoInitializeEx(NULL, COINIT_MULTITHREADED);
    19     
    20     CComPtr<IMyCar> spCar;
    21     spCar.CoCreateInstance(CLSID_MyCar);
    22 
    23     // use IDispatch
    24     DISPID PropertyID[3] = {0};
    25     BSTR PropName[3];
    26         
    27     PropName[0] = SysAllocString(L"AddGas");
    28     PropName[1] = SysAllocString(L"add");
    29     PropName[2] = SysAllocString(L"total");
    30     HRESULT hr = spCar->GetIDsOfNames(IID_NULL, PropName, 3, LOCALE_SYSTEM_DEFAULT, PropertyID);
    31 
    32     SysFreeString(PropName[0]);
    33     SysFreeString(PropName[1]);
    34     SysFreeString(PropName[2]);
    35 
    36     CComVariant avarParams[2];
    37     avarParams[1].vt = VT_I4;
    38     avarParams[1] = 4;
    39 
    40     LONG vTotal = 0;
    41     avarParams[0].vt = VT_I4 | VT_BYREF;
    42     avarParams[0] = &vTotal;
    43 
    44     DISPPARAMS params = { avarParams,
    45         NULL,              // Dispatch identifiers of named arguments. 
    46         2,                 // Number of arguments.
    47         0 };                // Number of named arguments.
    48 
    49     hr = spCar->Invoke(PropertyID[0], IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, ¶ms, NULL, NULL, NULL);
    50     
    51 
    52     DISPID PropertyID2[1] = { 0 };
    53     BSTR PropName2[1];
    54 
    55     PropName2[0] = SysAllocString(L"Gas");
    56     
    57     hr = spCar->GetIDsOfNames(IID_NULL, PropName2, 1, LOCALE_SYSTEM_DEFAULT, PropertyID2);
    58 
    59     SysFreeString(PropName2[0]);
    60 
    61 
    62     DISPPARAMS params2 = { NULL,
    63         NULL,
    64         0,
    65         0
    66     };
    67     
    68     CComVariant Result;
    69     hr = spCar->Invoke(PropertyID2[0], IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, ¶ms2, &Result, NULL, NULL);
    70     
    71     spCar.Release();
    72 
    73     CoUninitialize();
    74     
    75     return 0;
    76 }

    相关的COM组件的代码:

     1 STDMETHODIMP CMyCar::AddGas(LONG add, LONG* total)
     2 {
     3     // TODO: Add your implementation code here
     4     m_Gas += add;
     5     *total = m_Gas;
     6 
     7     return S_OK;
     8 }
     9 
    10 
    11 STDMETHODIMP CMyCar::get_Gas(LONG* pVal)
    12 {
    13     // TODO: Add your implementation code here
    14     *pVal = m_Gas;
    15 
    16     return S_OK;
    17 }

     上述摘自:http://blog.csdn.net/zj510/article/details/39494873

    引申(原创):

    1、CHtmlDialog调用js ,无参数有返回值情况:

    js函数如下:

    1 function GetFileChange()
    2 {
    3     return bFileChange;
    4 }

    MFC调用js函数如下:

     1 BOOL CHTMLDialogDlg::CallJSScript(const CString strFunc, _variant_t* pVarResult)
     2 {
     3     CComPtr<IDispatch> spScript;
     4     if(m_spHtmlDoc==NULL)
     5         return FALSE;
     6     HRESULT hr = m_spHtmlDoc->get_Script(&spScript);
     7     if(!SUCCEEDED(hr))
     8     {
     9         return FALSE;
    10     }
    11     CComBSTR bstrFunc(strFunc);
    12     DISPID dispid = NULL;
    13     HRESULT hrr = spScript->GetIDsOfNames(IID_NULL, &bstrFunc, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
    14     if(FAILED(hrr))
    15     {
    16         return FALSE;
    17     }
    18 
    19     DISPPARAMS dispparams;
    20     memset(&dispparams, 0, sizeof(dispparams));
    21     
    22     EXCEPINFO excepInfo;
    23     memset(&excepInfo, 0, sizeof(excepInfo));
    24     _variant_t vaResult;
    25     UINT nArgErr = (UINT)-1;
    26     hrr = spScript->Invoke(dispid, IID_NULL, 0, DISPATCH_METHOD, &dispparams, &vaResult, &excepInfo, &nArgErr);
    27     delete[] dispparams.rgvarg;
    28 
    29     if(FAILED(hrr))
    30     {
    31         return FALSE;
    32     }
    33     if(pVarResult)
    34     {
    35         *pVarResult = vaResult;
    36     }
    37     return TRUE;
    38 }

    调用如下

    1 if(CallJSScript("GetFileChange", &retParam)){
    2     if(retParam.vt == VT_BOOL){
    3         bFileChange = retParam.boolVal;
    4     }
    5  }

     2、CHtmlDialog调用js ,有参数有返回值情况:

    1 function OpenArgvFile(strArgvFile){
        .....
    2 return bRet; 3 }

    MFC调用js函数如下:

     1 BOOL CHtmlDialogDlg::CallJSScript(const CString strFunc, const CStringArray &paramArray, _variant_t* pVarResult)
     2 {
     3     CComPtr<IDispatch> spScript;
     4     if(m_spHtmlDoc==NULL)
     5         return FALSE;
     6     HRESULT hr = m_spHtmlDoc->get_Script(&spScript);
     7     if(!SUCCEEDED(hr))
     8     {
     9         return FALSE;
    10     }
    11     CComBSTR bstrFunc(strFunc);
    12     DISPID dispid = NULL;
    13     HRESULT hrr = spScript->GetIDsOfNames(IID_NULL, &bstrFunc, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
    14     if(FAILED(hrr))
    15     {
    16         return FALSE;
    17     }
    18 //
    19     const int arraySize = paramArray.GetSize();
    20 
    21     DISPPARAMS dispparams;
    22     memset(&dispparams, 0, sizeof dispparams);
    23     dispparams.cArgs = arraySize;
    24     dispparams.rgvarg = new VARIANT[dispparams.cArgs];
    25 
    26     for( int i = 0; i < arraySize; i++)
    27     {
    28         CComBSTR bstr = paramArray.GetAt(arraySize - 1 - i); // back reading
    29         bstr.CopyTo(&dispparams.rgvarg[i].bstrVal);
    30         dispparams.rgvarg[i].vt = VT_BSTR;
    31     }
    32     dispparams.cNamedArgs = 0;
    33 //
    34     //DISPPARAMS dispparams;
    35     //memset(&dispparams, 0, sizeof(dispparams));
    36     
    37     EXCEPINFO excepInfo;
    38     memset(&excepInfo, 0, sizeof(excepInfo));
    39     _variant_t vaResult;
    40     UINT nArgErr = (UINT)-1;
    41     hrr = spScript->Invoke(dispid, IID_NULL, 0, DISPATCH_METHOD, &dispparams, &vaResult, &excepInfo, &nArgErr);
    42     delete[] dispparams.rgvarg;
    43 
    44     if(FAILED(hrr))
    45     {
    46         return FALSE;
    47     }
    48     if(pVarResult)
    49     {
    50         *pVarResult = vaResult;
    51     }
    52     return TRUE;
    53 }

    调用如下

    1 CStringArray paramArray;
    2             _variant_t retParam;
    3             paramArray.Add(m_strArgvFile);
    4             if(!CallJSScript("OpenArgvFile", paramArray, &retParam)){...}

    附:

    CComPtr用法

    COM接口指针很危险,因为使用过程中需要每一个使用者都要严格并且正确的AddRef和Release,一旦出现问题,就会造成对象不能被正常释放,或者对象被重复删除,造成程序崩溃。所以使用COM接口,必须小心翼翼才行。
    但是,即使所有的代码中,都正确的AddRef和Release,也不一定能保证万无一失,例如:
    void SomeApp( IHello * pHello )
    {
    IHello* pCopy = pHello;
    pCopy->AddRef(); 
    OtherApp();
    pCopy->Hello();
    pCopy->Release();
    }
    看起来好像无懈可击,但是假设OtherApp中抛出了异常,那么pCopy->Release不就被跳过去了吗?
    幸好,所有的问题都从简单到复杂,再从复杂到简单的,因为我们有CComPtr!

    CComPtr被称为智能指针,是ATL提供的一个模版类,能够从语法上自动完成AddRef和Release。(源代码在atlbase.h中)
    CComPtr的用法很简单,以IHello*为例,将程序中所有接口指针类型(除了参数),都使用CComPtr<IHello> 代替即可。即程序中除了参数之外,再也不要使用IHello*,全部以CComPtr<IHello>代替。
    CComPtr的用法和普通COM指针几乎一样,另外使用中有以下几点需要注意。
    1. CComPtr已经保证了AddRef和Release的正确调用,所以不需要,也不能够再调用AddRef和Release。
    2. 如果要释放一个智能指针,直接给它赋NULL值即可。(这一点要牢记曾因为没有设置为null而出错)
    3. CComPtr本身析构的时候会释放COM指针。
    4. 当对CComPtr使用&运算符(取指针地址)的时候,要确保CComPtr为NUL。(因为通过CComPtr的地址对CComPtr赋值时,不会自动调用AddRef,若不为NULL,则前面的指针不能释放,CComPtr会使用assert报警)
    以刚才的程序为例:
    void SomeApp( IHello * pHello )
    {
    CComPtr<IHello> pCopy = pHello;
    OtherApp();
    pCopy->Hello();
    }
    由于pCopy是一个局部的对象,所以即使OtherApp()抛出异常,pCopy也会被析构,指针能够被释放。
    如果不想在程序临近发布前,还因为COM指针的引用计数造成崩溃的话,就牢记这一点吧:程序中除了参数之外,不要直接使用COM指针类型,一定要全部以CComPtr<IXXX>代替。

  • 相关阅读:
    竖版文字排列实现《金刚般若波罗蜜心经》
    前端气泡效果实现的方式
    纯CSS绘制三角形
    什么是块级格式上下文
    绝对定位元素left、right、top、bottom值与其margin和宽高的关系
    currentColor在CSS的含义
    HTML/css清除浮动的几种方式
    W3C中不同标准的含义
    table表格标签的属性
    输入你的生日某年某月某日,判断这一天是这一年的第几天、星期几?
  • 原文地址:https://www.cnblogs.com/jzxx/p/5713940.html
Copyright © 2011-2022 走看看