精华内容
下载资源
问答
  • 不依赖类型库,演示了基于MFC,自己查表实现IDispatch接口和调用IDispatch接口的过程。
  • NULL 博文链接:https://xizhenyin.iteye.com/blog/728073
  • 上回书介绍了两种方法来写自动化(IDispatch)接口的组件程序,一是用 MFC 方式编写“纯粹”的 IDispatch 接口;二是用 ATL 方式编写“双接口”的组件。这一回将讨论IDispatch 接口和双接口...... 使用者要想调用普通...
  • 创建可以使用IDispatch接口传递的动态对象
  • COM IDispatch接口封装

    2019-03-21 01:29:39
    NULL 博文链接:https://mengdejun.iteye.com/blog/932046
  • 实现自动化组件. 关键字:IDispatch,automation,自动化
  • 本代码演示了VC6环境下自动化组件的实现。 关键字:IDispatch,automation,自动化,双接口
  • 演示了使用MFC的分发映射表DISPATCH_MAP实现IDispatch接口和调用IDispatch接口的方法。
  • 基于ATL,演示了自己加载类型库导出ITypeInfo接口,借助ITypeInfo接口实现IDispatch接口的过程。
  • IDispatch接口原理与应用

    千次阅读 2018-10-30 15:33:55
    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!   ... IDispatch接口

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow

    也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!

                   

    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到注册表中搜索,可以搜索到如下结果:

     

    注意在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++类采用派生自该类,同时为了便于使用增加了很多成员函数的技术。

               

    给我老师的人工智能教程打call!http://blog.csdn.net/jiangjunshow

    这里写图片描述
    展开全文
  • 16.MFC实现IDispatch自动化接口

    千次阅读 2016-07-30 19:58:33
    本文演示了通用的不依赖类型库,自己查表实现IDispatch接口和借助MFC的分发映射表DISPATCH_MAP实现IDispatch接口的两种方法,提供了演示代码,据此可掌握不依赖类型库实现COM IDispatch接口的方法。

    前文叙述了IDispatch接口的原理,本文先讲MFC的实现细节,下文讲ATL的实现细节。

    1.通用方法

    MFC不使用类型库,这里先讲不用类型库实现IDispatch,此时一般实现GetIDsOfNames和Invoke函数。这里使用MFC实现,实际上在ATL中也可以使用。

    按照之前讲的通用接口的编写方法,定义嵌入类和工厂类声明如下,嵌入类实现了IDispatch接口。

    	//接口映射表
    	BEGIN_INTERFACE_PART(Cat, IDispatch)
    		INIT_INTERFACE_PART(CAnimalObject, Cat)
    
    		virtual HRESULT STDMETHODCALLTYPE GetTypeInfoCount( 
    		/* [out] */ __RPC__out UINT *pctinfo);
    
    		virtual HRESULT STDMETHODCALLTYPE GetTypeInfo( 
    			/* [in] */ UINT iTInfo,
    			/* [in] */ LCID lcid,
    			/* [out] */ __RPC__deref_out_opt ITypeInfo **ppTInfo);
    
    		virtual HRESULT STDMETHODCALLTYPE GetIDsOfNames( 
    			/* [in] */ __RPC__in REFIID riid,
    			/* [size_is][in] */ __RPC__in_ecount_full(cNames) LPOLESTR *rgszNames,
    			/* [range][in] */ UINT cNames,
    			/* [in] */ LCID lcid,
    			/* [size_is][out] */ __RPC__out_ecount_full(cNames) DISPID *rgDispId);
    
    		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);
    	END_INTERFACE_PART_STATIC(Cat)
    
    	DECLARE_INTERFACE_MAP()

    建立接口映射表如下

    //接口映射表
    BEGIN_INTERFACE_MAP(CAnimalObject, CCmdTarget)
    	INTERFACE_PART(CAnimalObject, IID_IDispatch, Cat)
    END_INTERFACE_MAP()


    为了不依赖类型库实现IDispatch接口,建立名字和DispID如下

    //建立Dispatch表
    map<CString, UINT> g_DispMap;
    
    CAnimalObject::CAnimalObject(void)
    {
    	g_DispMap[L"SayHello1"] = DISP_ID_SAYHELLO1;
    	g_DispMap[L"SayHello2"] = DISP_ID_SAYHELLO2;
    }


    由于没有类型库,则GetTypeInfoCount和GetTypeInfo不用实现,具体如下:

    HRESULT STDMETHODCALLTYPE CAnimalObject::XCat::GetTypeInfoCount( 
    	/* [out] */ __RPC__out UINT *pctinfo)
    {
    	*pctinfo = 0;	//没有类型库
    	return S_OK;
    }
    
    HRESULT STDMETHODCALLTYPE CAnimalObject::XCat::GetTypeInfo( 
    	/* [in] */ UINT iTInfo,
    	/* [in] */ LCID lcid,
    	/* [out] */ __RPC__deref_out_opt ITypeInfo **ppTInfo)
    {
    	*ppTInfo = NULL;
    	return E_NOTIMPL;
    }
    


    GetIDsOfNames的实现只需要查表即可,如下:

    HRESULT STDMETHODCALLTYPE CAnimalObject::XCat::GetIDsOfNames( 
    	/* [in] */ __RPC__in REFIID riid,
    	/* [size_is][in] */ __RPC__in_ecount_full(cNames) LPOLESTR *rgszNames,
    	/* [range][in] */ UINT cNames,
    	/* [in] */ LCID lcid,
    	/* [size_is][out] */ __RPC__out_ecount_full(cNames) DISPID *rgDispId)
    {
    	
    	for (UINT i=0; i<cNames; i++)
    	{
    		map<CString, UINT>::iterator iter = g_DispMap.find(rgszNames[i]);
    		if ( g_DispMap.end() != iter )
    		{
    			rgDispId[i] = iter->second;
    		}
    		else
    		{
    			rgDispId[i] = DISPID_UNKNOWN;
    		}
    	}
    
    	return S_OK;
    }

    这里可能存在一次性传入多个Name的情况,此时cNames标示传入的name个数,rgszNames和rgDispID均为数组。


    Invoke根据传入的分发ID,调用不同的逻辑:

    HRESULT STDMETHODCALLTYPE CAnimalObject::XCat::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)
    {
    	if (0==dispIdMember ||
    		(dispIdMember!=DISP_ID_SAYHELLO1 && dispIdMember!=DISP_ID_SAYHELLO2) ||
    		0==(DISPATCH_METHOD&wFlags))
    	{
    		return E_NOTIMPL;
    	}
    
    	if (pVarResult)
    	{
    		CComVariant var(true);
    		*pVarResult = var;
    	}
    
    	USES_CONVERSION;
    
    	switch (dispIdMember)
    	{
    	case DISP_ID_SAYHELLO1:
    		if (pDispParams &&							//参数数组有效
    			pDispParams->cArgs==1 &&				//参数个数为1
    			pDispParams->rgvarg[0].vt==VT_BSTR &&	//参数类型满足
    			pDispParams->rgvarg[0].bstrVal)			//参数值有效
    		{
    			 CString strVal(OLE2T(pDispParams->rgvarg[0].bstrVal));
    			 wcout << L"猫猫说我的名字叫:" << strVal.GetBuffer(0) << endl;
    		}
    		break;
    
    	case DISP_ID_SAYHELLO2:
    		if (pDispParams &&							//参数数组有效
    			pDispParams->cArgs==1 &&				//参数个数为1
    			pDispParams->rgvarg[0].vt==VT_I4 &&		//参数类型满足
    			pDispParams->rgvarg[0].intVal)			//参数值有效
    		{
    			wcout << L"猫猫说我的年龄是:" << pDispParams->rgvarg[0].intVal << endl;
    		}
    		break;
    	}
    
    	return S_OK;
    }

    2.标准MFC的实现方法

    MFC中我们已经见到了各种查表,如消息映射表MESSAGE_MAP,接口映射表INTERFACE|_MAP等。同样为了支持IDISPATC接口,MFC做了一套分发映射表DISPATCH_MAP,和之前的使用方法一样。

    另外,MFC中的CCmdTarget默认实现了IDispatch接口,只要我们在子类构造函数调用EnableAutomation开启自动化支持即可。此时不用再单独添加接口映射表,MFC已默认将IDispatch接口加到接口查询表中。


    MFC这套机制非常简单,如下:

    声明分发映射表:

    	//分派映射表
    	DECLARE_DISPATCH_MAP()
    实现分发映射表:

    //分配映射表
    BEGIN_DISPATCH_MAP(CAnimalObject, CCmdTarget)
    	DISP_FUNCTION_ID(CAnimalObject, "SayHello1", DISP_ID_SAYHELLO1, SayHello1, VT_I4, VTS_BSTR)// "SayHello1"不要加L前缀
    	DISP_FUNCTION_ID(CAnimalObject, "SayHello2", DISP_ID_SAYHELLO2, SayHello2, VT_I4, VTS_I4)
    END_DISPATCH_MAP()

    DISP_FUNCTION_ID宏参数分别为当前类名,函数名,分发ID,函数指针,函数返回值,函数参数


    对应的调用函数逻辑实现如下:

    BOOL CAnimalObject::SayHello1( BSTR szWord )
    {
    	USES_CONVERSION;
    	CString strWord(OLE2CW(szWord));
    
    	wcout << L"猫猫2的名字:" << strWord.GetBuffer(0) << endl;
    	return TRUE;
    }
    
    BOOL CAnimalObject::SayHello2( int nAge )
    {
    	wcout << L"猫猫2的年龄:" << nAge << endl;
    	return TRUE;
    }

    3.调用IDispatch接口

    默认的IDispatch接口调用Invoke函数时参数太繁琐,MFC提供COleDispatchDriver类来辅助操作,如下:

    		//初始化COM库
    		if (CoInitialize(NULL) != S_OK)
    		{
    			wcout << L"Fail to Initialize COM" << endl;
    			return -1;
    		}
    
    		//自动化调用
    		COleDispatchDriver d;
    		if (d.CreateDispatch(CLSID_AnimalObject))
    		{
    			BYTE params1[] = {VTS_BSTR};
    			BYTE params2[] = {VTS_I4};
    			BOOL bRet;
    
    			d.InvokeHelper(DISP_ID_SAYHELLO1, DISPATCH_METHOD, VT_I4, (LPVOID)&bRet, params1, L"maomao");
    			d.InvokeHelper(DISP_ID_SAYHELLO2, DISPATCH_METHOD, VT_I4, (LPVOID)&bRet, params2, 20);
    
    			d.ReleaseDispatch();
    		}	
    
    		::CoUninitialize();

    InvokeHelper参数依次为分发ID,方法Flag,返回类型,返回值,函数参数类型数组,函数参数。


    IDispatch通用实现方法下载链接

    IDispatch MFC实现方法下载链接

    原创,转载请注明来自http://blog.csdn.net/wenzhou1219

    展开全文
  • DirectShowPlayerService::doRender: Unresolved error code 0x80040266 (IDispatch error #102)

    DirectShowPlayerService::doRender: Unresolved error code 0x80040266 (IDispatch error #102)

    https://blog.csdn.net/mmmmmmyy/article/details/81877233

    https://blog.csdn.net/u012552296/article/details/89295273

    展开全文
  • IDispatch接口介绍

    2016-05-23 18:21:00
    1.C程序调用时,调用者必须预先知道接口规范(如,参数类型、参数...2.IDispatch要求其实例必须自我描述,即拿到一个对象后,可从对象中直接获取调用方式,而无须预先明确。 3.IDispatch中通过VT_TYPE来指定相关...

    1.         C程序调用时,调用者必须预先知道接口规范(如,参数类型、参数字节长度、参数顺序等)。由于不同语言这些规范有所不同,COM未解决不同语言之间调用,提供了IDispatch接口。

    2.         IDispatch要求其实例必须自我描述,即拿到一个对象后,可从对象中直接获取调用方式,而无须预先明确。

    3.         IDispatch中通过VT_TYPE来指定相关类型,如 VT_I4为4字节整形、VT_BSTR为unicode字符串,VT_DISPATCH表示是一个IDispatch对象

    4.         给对象中每一属性或函数(Method)分配一个整形Id和一个字符串name,调用者可以通过name字符串确定如何调用。如,若name为"length"的属性,调用者就理解为长度。由于这里通常是根据name来理解相应属性,因此name描述应足够准确。如,以"length()"为名称的函数实现整数相加功能就是不恰当的。

    5.         使用IDispatch对象时,首相调用 IDispatch::GetIDsOfNames()将属性、函数名称作为参数,获取对应的属性、函数id。

    6.         再调用IDispatch::Invoke() 将id作为参数,实际调用功能。

    7.         若为获取属性值,则 Invoke()调用时,传入 Dispatch_PropertyGet标志。

    8.         若为设置属性值,则Invoke()调用时,传入 Dispatch_PropertyPut标志。并在 DispParams参数中指定修该属性改为何值。DispParams结构说明见后。

    9.         若为调用函数,则 Invoke()调用时,传入 Dispatch_Method标志。若该Method需要参数,则通过IDispatch::Invoke()的DispParams参数指定。

    10.     DispParams结构使用举例:

    DISPPARAMS dispparams;

             dispparams.rgdispidNamedArgs = &dispidOfNamedArgs;

             dispparams.cArgs = 1;

             dispparams.cNamedArgs = 1;

             dispparams.rgvarg = new VARIANTARG[1];

             dispparams.rgvarg[0].vt = VT_I4;

             dispparams.rgvarg[0].intVal = 123;

    a.         上面代码是一个用于给Method传参的简单例子,先创建一个DispParams对象

    b.         cArgs指定Method中的参数个数。

    c.         cNamedArgs指定Method中已经命名的参数个数。(命名参数是对应无名参数的概念。有些语言可定义不定参数,此时IDispatch的描述中不会给参数分配名称,而是调用时以无名参数存在。如,JS中 Array对象的push()方法,可支持不定个数的参数)

    d.         rgvarg 为实际参数数组,每一元素表示一个参数,其中.vt表明此元素的数据类型,intVal项是一个C++联合结构,如vt == VT_I4时,应以intVal = xxx方式赋值;若 vt == VT_BSTR,则应以 bstrVal = xxx方式赋值

    11.     举例:两个参数,都是无名称参数,第一个为整形,第二个为BSTR型

    DISPPARAMS dispparams;

             dispparams.rgdispidNamedArgs = NULL;

             dispparams.cArgs = 2;

             dispparams.cNamedArgs = 0;

             dispparams.rgvarg = new VARIANTARG[2]; // 2个参数,分配2个空间

             dispparams.rgvarg[0].vt = VT_I4;  // 整形

    dispparams.rgvarg[0].intVal = 123;

    dispparams.rgvarg[1].vt = VT_BSTR; // 字符串型

    dispparams.rgvarg[1].bstrVal = L"abcd";

     

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

     

    [cpp]  view plain  copy
     
     在CODE上查看代码片派生到我的代码片
    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型:

     

    [cpp]  view plain  copy
     
     在CODE上查看代码片派生到我的代码片
    1. typedef LONG DISPID;  

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

     

     

    [html]  view plain  copy
     
     在CODE上查看代码片派生到我的代码片
    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,它有两个参数,一个输入,一个输出。它的实现基本如下:

     

     

    [cpp]  view plain  copy
     
     在CODE上查看代码片派生到我的代码片
    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,看下面的代码。数组里面的第一个元素是方法名字,第二个/第三个是参数名字。

     

    [cpp]  view plain  copy
     
     在CODE上查看代码片派生到我的代码片
    1. CComPtr<IMyCar> spCar;  
    2. spCar.CoCreateInstance(CLSID_MyCar);  

     

     

    [cpp]  view plain  copy
     
     在CODE上查看代码片派生到我的代码片
    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里面非常重要的一样函数,方法调用就靠这个函数了。函数原型:

     

    [cpp]  view plain  copy
     
     在CODE上查看代码片派生到我的代码片
    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截来的。

     

    先来看一个调用例子:

     

    [cpp]  view plain  copy
     
     在CODE上查看代码片派生到我的代码片
    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。

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

     

    [cpp]  view plain  copy
     
     在CODE上查看代码片派生到我的代码片
    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);  


    运行可以得到结果:

     


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

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

     

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

    完整客户端代码:

     

    [cpp]  view plain  copy
     
     在CODE上查看代码片派生到我的代码片
    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组件的代码:

     

     

    [cpp]  view plain  copy
     
     在CODE上查看代码片派生到我的代码片
      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. }  

    转载于:https://www.cnblogs.com/jzxx/p/5521026.html

    展开全文
  • 17.ATL实现IDispatch自动化接口

    千次阅读 2016-07-30 20:37:48
    本文基于ATL,演示了自己加载类型库导出ITypeInfo接口,借助ITypeInfo接口实现IDispatch接口的过程和ATL自己利用IDispatchImpl实现IDispatch的方法,据此可以掌握依赖类型库实现IDispatch接口的方法。
  • ATL IDispatch调度接口

    2017-08-11 10:37:18
    转载自:... 目录(?)[+] ...IDispatch接口:完成接口调度的接口,可以不使用头文件完成接口的函数调用 IDispatch 接口函数  1> GetIDsOfNames-根据接口函数的名称获取接口函数的ID 2>
  • COM组件 IDispatch 及双接口的调用

    千次阅读 2016-01-21 15:41:00
    上回书介绍了两种方法来写自动化(IDispatch)接口的组件程序,一是用 MFC 方式编写“纯粹”的IDispatch 接口;二是用 ATL 方式编写“双接口”的组件。 二、IDispatch 接口和双接口 使用者
  • 大部分异常都是由于SQL语句错误导致的,以下列出几种常见错误: 1.SQL语句内容缺失’’,例如 insert into AA ( NUM1,NUM2,NUM3,) values (' 12','34',56) 2.使用insert但是在表中已经有当前数据也会报错 ...
  • ppt文档 适于学习IDispatch与自动化、ActiveX的基础知识
  • 可以在VC++中如下调用IDispatch接口中的属性和方法 //获取属性值 _bstr_t bstrValue=CVB::get(pDispatch,L\"Value\"); //设置属性值 CVB::put(pDispatch,L\"Value\",bstrValue); //调用方法 _bstr_t bstrMessage2...
  • IDispatch

    2014-05-29 13:39:00
    BOOL VARIANTToDouble(VARIANTARG * pVarSrc, double * pd) { VARIANTARG dest; VARIANTINIT(&dest); HRESULT hr = VariantChangeType(pVarSrc, dest, 0, VT_R8); if (FAILED(hr)) ... ...
  • IDispatch 接口方法的调用(领悟版)_com_dispatch_method解释 在COM开发中免不了要使用 IDispatch 接口,这个接口在脚本中对应的就是 Object 对象,经常在调用一个对象的方法或者使用他的属性的时候最容易的方法就是...
  • IDisPatch接口调用分析

    2011-06-07 22:28:34
    ATL中关于如何实现IDispatch接口,以及如何调用IDispatch接口的详尽代码分析
  • 在我们自己写的自动化组件中,当然你可以把所有的方法都用唯一的一个自动化接口表现出来,但是这样好吗?所有的方法都处于一个层次上,没有适当的分类,...关键字:idispatch,lpdispatch,ole,automation,自动化,接口
  • ./test.xlsx)就会出现下面的错误: QAxBase: Error calling IDispatch member Open: Exception thrown by server Code : -2146827284 Source : Microsoft Excel Description: ??????? ./test.xlsx???????????????? ...
  • IDispatch是由OLE自动化协议暴露出来的接口。 IDispatch可以由IUnknown得到,并且在IUnknown本身所含有三个方法(AddRef,Release和QueryInterface)上增加另外四个方法(GetTypeInfoCount,GetTypeInfo,...
  • 其实,IDispatch这个接口本身也很简单,只有4个方法:   [cpp] view plain copy   IDispatch : public IUnknown  {  public:   virtual HRESULT STDMETHODCALLTYPE GetTypeInfoCount(   /* [o...
  • workbook->dynamicCall(“SaveAs (const QString &)”, file);file是不能用’/’,如果调QCoreApplication::applicationDirPath()等QT的API获取路径的,都需要调replace(’/’, ‘\’)把’/‘替换成’\’,...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 13,256
精华内容 5,302
关键字:

idispatch