热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

求教:Idispatch::invoke内部实现原理

如题,书上说内部实现如同一堆switch(),根据方法ID对应相应的函数调用。我觉得,自己实现的话,自然如此。可是ATL内部如何能知道用户方法的参数个数和参数类型的呢?虽然ATL可以做
如题,书上说内部实现如同一堆switch(),根据方法ID 对应相应的函数调用。
我觉得,自己实现的话,自然如此。 可是ATL内部如何能知道用户方法的参数个数和参数类型的呢?

虽然ATL可以做到方法ID和函数指针的映射关系。  可是,参数类型是无法预知的,参数个数也是未知的,ATL如何做到“预定义”如此复杂的一个switch()结构呢? 这里的重点是“预定义”,ATL不可能边解析参数个数和类型,边自然形成switch()的吧

求教, 多谢

17 个解决方案

#1


顶。。。。。。。。

#2


在编译类型库的时候,这些都已经定义了。

#3


引用 2 楼 Saleayas 的回复:
在编译类型库的时候,这些都已经定义了。

比如函数指针调用,需要有函数原型,传入参数才能使用。 我觉得,函数原型COM不可能提前知道的啊 。如果在编译类型库的时候确定,难道COM的源代码能实现自动编码?

#4


 DISPPARAMS* pdispparams 不就是参数么

#5


引用 4 楼 akirya 的回复:
 DISPPARAMS* pdispparams 不就是参数么

是参数,解析出参数,调用函数指针,那“函数原型” COM是如何提前预知的?

#6


引用 5 楼 u013553458 的回复:
Quote: 引用 4 楼 akirya 的回复:

 DISPPARAMS* pdispparams 不就是参数么

是参数,解析出参数,调用函数指针,那“函数原型” COM是如何提前预知的?

不需要知道啊,把参数压栈就行了。出错是调用者的问题。

#7


Invoke参数说明:
1. DISPID dispIdMember : 标志客户待调用的函数名,可由GetIDsOfNames获得
2. REFIID riid : 必须为 IID_NULL
3. LCID lcid : 用户本地化信息,可用 GetUserDefaultLCID() 获取
4. WORD wFlags : 一个函数名称其实可以和四个函数关联 (常规函数,设置属性函数,通过引用设置属性函数,获取属性函数),
                 它的值可以是DISPATCH_METHOD, DISPATCH_PROPERTYPUT, DISPATCH_PROPERTYPUTREF, DISPATCH_PROPERTYGET.
5. DISPPARAMS *pDispParams : 参数列表,其定义如下:

[cpp] view plaincopy
typedef struct tagDISPPARAMS  
    {  
    /* [size_is] */ VARIANTARG *rgvarg; //与VARIANT相同. 所以自动控制程序能支持的类型有限  
    /* [size_is] */ DISPID *rgdispidNamedArgs; //命名参数,C++中不用,VB支持  
    UINT cArgs; //参数个数  
    UINT cNamedArgs;  
    }   DISPPARAMS;  

6. VARIANT *pVarResult :保存函数或propget的结果,没有返回值时为NULL
7. EXCEPINFO *pExcepInfo :保存例外情况的信息,可参考C++异常处理。当Invoke返回DISP_E_EXCEPTION,DISP_E_PARAMNOTFOUND等
                           时,可查询pExcepInfo中相关信息。

#8


在类型库中会知道每一个参数。
然后,把这些参数构造调用堆栈,然后,直接呼叫。
不使用C代码编译。

#9


引用 6 楼 akirya 的回复:
Quote: 引用 5 楼 u013553458 的回复:

Quote: 引用 4 楼 akirya 的回复:

 DISPPARAMS* pdispparams 不就是参数么

是参数,解析出参数,调用函数指针,那“函数原型” COM是如何提前预知的?

不需要知道啊,把参数压栈就行了。出错是调用者的问题。


你是说,有这样的API函数么? 不需要函数声明直接压栈的。   或者是,ATL直接使用汇编码搞定的?

#10


引用 8 楼 Saleayas 的回复:
在类型库中会知道每一个参数。
然后,把这些参数构造调用堆栈,然后,直接呼叫。
不使用C代码编译。

不使用C代码编译,是不是意味着COM调用的相关部分是汇编码搞定的么? 我很难理解,不需要源码即可调用的机制,能不能介绍一下

#11


引用 9 楼 u013553458 的回复:
Quote: 引用 6 楼 akirya 的回复:

Quote: 引用 5 楼 u013553458 的回复:

Quote: 引用 4 楼 akirya 的回复:

 DISPPARAMS* pdispparams 不就是参数么

是参数,解析出参数,调用函数指针,那“函数原型” COM是如何提前预知的?

不需要知道啊,把参数压栈就行了。出错是调用者的问题。


你是说,有这样的API函数么? 不需要函数声明直接压栈的。   或者是,ATL直接使用汇编码搞定的?

这个实现不在ATL里面

内嵌汇编是最简单的实现方式了。
push ..
push ..
call ..

#12


引用 11 楼 akirya 的回复:
Quote: 引用 9 楼 u013553458 的回复:

Quote: 引用 6 楼 akirya 的回复:

Quote: 引用 5 楼 u013553458 的回复:

Quote: 引用 4 楼 akirya 的回复:

 DISPPARAMS* pdispparams 不就是参数么

是参数,解析出参数,调用函数指针,那“函数原型” COM是如何提前预知的?

不需要知道啊,把参数压栈就行了。出错是调用者的问题。


你是说,有这样的API函数么? 不需要函数声明直接压栈的。   或者是,ATL直接使用汇编码搞定的?

这个实现不在ATL里面

内嵌汇编是最简单的实现方式了。
push ..
push ..
call ..

多谢

#13


微软的一个例子中的 Invoke 实现

STDMETHODIMP
CPoly::Invoke(
              DISPID dispidMember,
              REFIID riid,
              LCID lcid,
              unsigned short wFlags,
              DISPPARAMS* pdispparams,
              VARIANT* pvarResult,
              EXCEPINFO* pexcepinfo,
              unsigned int* puArgErr)
{
  HRESULT hresult;
  VARIANTARG varg0, varg1;
  VARIANT varResultDummy;

  UNUSED(lcid);
  UNUSED(pexcepinfo);

  if(wFlags & ~(DISPATCH_METHOD | DISPATCH_PROPERTYGET | DISPATCH_PROPERTYPUT | DISPATCH_PROPERTYPUTREF))
    return E_INVALIDARG;

  // this object only exposes a "default" interface.
  //
  if(!IsEqualIID(riid, IID_NULL))
    return DISP_E_UNKNOWNINTERFACE;

  // This makes the following code a bit simpler if the caller
  // happens to be ignoring the return value. Some implementations
  // may choose to deal with this differently.
  //
  if(pvarResult == (VARIANT*)NULL)
    pvarResult = &varResultDummy;

  VariantInit(&varg0);
  VariantInit(&varg1);

  // assume the return type is void, unless we find otherwise.
  VariantInit(pvarResult);

  switch(dispidMember){
    case IDMEMBER_CPOLY_DRAW:
      Draw();
      break;

    case IDMEMBER_CPOLY_RESET:
      Reset();
      break;

    case IDMEMBER_CPOLY_DUMP:
      Dump();
      break;

    case IDMEMBER_CPOLY_QUIT:
      Quit();
      break;

    case IDMEMBER_CPOLY_ADDPOINT:
      hresult = DispGetParam(pdispparams, 0, VT_I2, &varg0, puArgErr);
      if(hresult != NOERROR)
        return hresult;

      hresult = DispGetParam(pdispparams, 1, VT_I2, &varg1, puArgErr);
      if(hresult != NOERROR)
        return hresult;

      hresult = AddPoint(V_I2(&varg1), V_I2(&varg0));
      if(hresult != NOERROR)
        return hresult;
      break;

    case IDMEMBER_CPOLY_ENUMPOINTS:
      IEnumVARIANT* penum;

      hresult = EnumPoints(&penum);
      if(hresult != NOERROR)
        return hresult;

      V_VT(pvarResult) = VT_UNKNOWN;
      hresult = penum->QueryInterface(
        IID_IUnknown, (void**)&V_UNKNOWN(pvarResult));
      if(hresult != NOERROR)
        return hresult;
      penum->Release();
      break;

    case IDMEMBER_CPOLY_GETXORIGIN:
      V_VT(pvarResult) = VT_I2;
      V_I2(pvarResult) = m_xorg;
      break;

    case IDMEMBER_CPOLY_SETXORIGIN:
      hresult = DispGetParam(pdispparams, 0, VT_I2, &varg0, puArgErr);
      if(hresult != NOERROR)
        return hresult;
      m_xorg = V_I2(&varg0);
      break;

    case IDMEMBER_CPOLY_GETYORIGIN:
      V_VT(pvarResult) = VT_I2;
      V_I2(pvarResult) = m_yorg;
      break;

    case IDMEMBER_CPOLY_SETYORIGIN:
      hresult = DispGetParam(pdispparams, 0, VT_I2, &varg0, puArgErr);
      if(hresult != NOERROR)
        return hresult;
      m_yorg = V_I2(&varg0);
      break;

    case IDMEMBER_CPOLY_GETWIDTH:
      V_VT(pvarResult) = VT_I2;
      V_I2(pvarResult) = GetWidth();
      break;

    case IDMEMBER_CPOLY_SETWIDTH:
      hresult = DispGetParam(pdispparams, 0, VT_I2, &varg0, puArgErr);
      if(hresult != NOERROR)
        return hresult;
      SetWidth(V_I2(&varg0));
      break;

    case IDMEMBER_CPOLY_GETRED:
      V_VT(pvarResult) = VT_I2;
      V_I2(pvarResult) = get_red();
      break;

    case IDMEMBER_CPOLY_SETRED:
      hresult = DispGetParam(pdispparams, 0, VT_I2, &varg0, puArgErr);
      if(hresult != NOERROR)
        return hresult;
      set_red(V_I2(&varg0));
      break;

    case IDMEMBER_CPOLY_GETGREEN:
      V_VT(pvarResult) = VT_I2;
      V_I2(pvarResult) = get_green();
      break;

    case IDMEMBER_CPOLY_SETGREEN:
      hresult = DispGetParam(pdispparams, 0, VT_I2, &varg0, puArgErr);
      if(hresult != NOERROR)
        return hresult;
      set_green(V_I2(&varg0));
      break;

    case IDMEMBER_CPOLY_GETBLUE:
      V_VT(pvarResult) = VT_I2;
      V_I2(pvarResult) = get_blue();
      break;

    case IDMEMBER_CPOLY_SETBLUE:
      hresult = DispGetParam(pdispparams, 0, VT_I2, &varg0, puArgErr);
      if(hresult != NOERROR)
        return hresult;
      set_blue(V_I2(&varg0));
      break;

    default:
      return DISP_E_MEMBERNOTFOUND;
  }

  return NOERROR;
}

#14


引用 13 楼 Dobzhansky 的回复:
微软的一个例子中的 Invoke 实现

STDMETHODIMP
CPoly::Invoke(
              DISPID dispidMember,
              REFIID riid,
              LCID lcid,
              unsigned short wFlags,
              DISPPARAMS* pdispparams,
              VARIANT* pvarResult,
              EXCEPINFO* pexcepinfo,
              unsigned int* puArgErr)
{
  HRESULT hresult;
  VARIANTARG varg0, varg1;
  VARIANT varResultDummy;

  UNUSED(lcid);
  UNUSED(pexcepinfo);

  if(wFlags & ~(DISPATCH_METHOD | DISPATCH_PROPERTYGET | DISPATCH_PROPERTYPUT | DISPATCH_PROPERTYPUTREF))
    return E_INVALIDARG;

  // this object only exposes a "default" interface.
  //
  if(!IsEqualIID(riid, IID_NULL))
    return DISP_E_UNKNOWNINTERFACE;

  // This makes the following code a bit simpler if the caller
  // happens to be ignoring the return value. Some implementations
  // may choose to deal with this differently.
  //
  if(pvarResult == (VARIANT*)NULL)
    pvarResult = &varResultDummy;

  VariantInit(&varg0);
  VariantInit(&varg1);

  // assume the return type is void, unless we find otherwise.
  VariantInit(pvarResult);

  switch(dispidMember){
    case IDMEMBER_CPOLY_DRAW:
      Draw();
      break;

    case IDMEMBER_CPOLY_RESET:
      Reset();
      break;

    case IDMEMBER_CPOLY_DUMP:
      Dump();
      break;

    case IDMEMBER_CPOLY_QUIT:
      Quit();
      break;

    case IDMEMBER_CPOLY_ADDPOINT:
      hresult = DispGetParam(pdispparams, 0, VT_I2, &varg0, puArgErr);
      if(hresult != NOERROR)
        return hresult;

      hresult = DispGetParam(pdispparams, 1, VT_I2, &varg1, puArgErr);
      if(hresult != NOERROR)
        return hresult;

      hresult = AddPoint(V_I2(&varg1), V_I2(&varg0));
      if(hresult != NOERROR)
        return hresult;
      break;

    case IDMEMBER_CPOLY_ENUMPOINTS:
      IEnumVARIANT* penum;

      hresult = EnumPoints(&penum);
      if(hresult != NOERROR)
        return hresult;

      V_VT(pvarResult) = VT_UNKNOWN;
      hresult = penum->QueryInterface(
        IID_IUnknown, (void**)&V_UNKNOWN(pvarResult));
      if(hresult != NOERROR)
        return hresult;
      penum->Release();
      break;

    case IDMEMBER_CPOLY_GETXORIGIN:
      V_VT(pvarResult) = VT_I2;
      V_I2(pvarResult) = m_xorg;
      break;

    case IDMEMBER_CPOLY_SETXORIGIN:
      hresult = DispGetParam(pdispparams, 0, VT_I2, &varg0, puArgErr);
      if(hresult != NOERROR)
        return hresult;
      m_xorg = V_I2(&varg0);
      break;

    case IDMEMBER_CPOLY_GETYORIGIN:
      V_VT(pvarResult) = VT_I2;
      V_I2(pvarResult) = m_yorg;
      break;

    case IDMEMBER_CPOLY_SETYORIGIN:
      hresult = DispGetParam(pdispparams, 0, VT_I2, &varg0, puArgErr);
      if(hresult != NOERROR)
        return hresult;
      m_yorg = V_I2(&varg0);
      break;

    case IDMEMBER_CPOLY_GETWIDTH:
      V_VT(pvarResult) = VT_I2;
      V_I2(pvarResult) = GetWidth();
      break;

    case IDMEMBER_CPOLY_SETWIDTH:
      hresult = DispGetParam(pdispparams, 0, VT_I2, &varg0, puArgErr);
      if(hresult != NOERROR)
        return hresult;
      SetWidth(V_I2(&varg0));
      break;

    case IDMEMBER_CPOLY_GETRED:
      V_VT(pvarResult) = VT_I2;
      V_I2(pvarResult) = get_red();
      break;

    case IDMEMBER_CPOLY_SETRED:
      hresult = DispGetParam(pdispparams, 0, VT_I2, &varg0, puArgErr);
      if(hresult != NOERROR)
        return hresult;
      set_red(V_I2(&varg0));
      break;

    case IDMEMBER_CPOLY_GETGREEN:
      V_VT(pvarResult) = VT_I2;
      V_I2(pvarResult) = get_green();
      break;

    case IDMEMBER_CPOLY_SETGREEN:
      hresult = DispGetParam(pdispparams, 0, VT_I2, &varg0, puArgErr);
      if(hresult != NOERROR)
        return hresult;
      set_green(V_I2(&varg0));
      break;

    case IDMEMBER_CPOLY_GETBLUE:
      V_VT(pvarResult) = VT_I2;
      V_I2(pvarResult) = get_blue();
      break;

    case IDMEMBER_CPOLY_SETBLUE:
      hresult = DispGetParam(pdispparams, 0, VT_I2, &varg0, puArgErr);
      if(hresult != NOERROR)
        return hresult;
      set_blue(V_I2(&varg0));
      break;

    default:
      return DISP_E_MEMBERNOTFOUND;
  }

  return NOERROR;
}


是的,这也是我的疑问,上面代码用到的函数都是已知的,然而,我们写的函数原型对COM来说是未知的,这就是问题所在。 所以, 内部用汇编直接压栈比较合理

#15


顶,继续等待愿意回答的朋友解答,谢谢

#16


第一个,IDispatch所支持的数据类型是有限的
第二个,IDispatch的实现需要事先有typelib

#17


http://blog.csdn.net/amwfnyq/article/details/7829032

推荐阅读
  • 本文介绍了一个在线急等问题解决方法,即如何统计数据库中某个字段下的所有数据,并将结果显示在文本框里。作者提到了自己是一个菜鸟,希望能够得到帮助。作者使用的是ACCESS数据库,并且给出了一个例子,希望得到的结果是560。作者还提到自己已经尝试了使用"select sum(字段2) from 表名"的语句,得到的结果是650,但不知道如何得到560。希望能够得到解决方案。 ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • 本文介绍了贝叶斯垃圾邮件分类的机器学习代码,代码来源于https://www.cnblogs.com/huangyc/p/10327209.html,并对代码进行了简介。朴素贝叶斯分类器训练函数包括求p(Ci)和基于词汇表的p(w|Ci)。 ... [详细]
  • 微软头条实习生分享深度学习自学指南
    本文介绍了一位微软头条实习生自学深度学习的经验分享,包括学习资源推荐、重要基础知识的学习要点等。作者强调了学好Python和数学基础的重要性,并提供了一些建议。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 展开全部下面的代码是创建一个立方体Thisexamplescreatesanddisplaysasimplebox.#Thefirstlineloadstheinit_disp ... [详细]
  • 本文详细介绍了Spring的JdbcTemplate的使用方法,包括执行存储过程、存储函数的call()方法,执行任何SQL语句的execute()方法,单个更新和批量更新的update()和batchUpdate()方法,以及单查和列表查询的query()和queryForXXX()方法。提供了经过测试的API供使用。 ... [详细]
  • 利用Visual Basic开发SAP接口程序初探的方法与原理
    本文介绍了利用Visual Basic开发SAP接口程序的方法与原理,以及SAP R/3系统的特点和二次开发平台ABAP的使用。通过程序接口自动读取SAP R/3的数据表或视图,在外部进行处理和利用水晶报表等工具生成符合中国人习惯的报表样式。具体介绍了RFC调用的原理和模型,并强调本文主要不讨论SAP R/3函数的开发,而是针对使用SAP的公司的非ABAP开发人员提供了初步的接口程序开发指导。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • Go Cobra命令行工具入门教程
    本文介绍了Go语言实现的命令行工具Cobra的基本概念、安装方法和入门实践。Cobra被广泛应用于各种项目中,如Kubernetes、Hugo和Github CLI等。通过使用Cobra,我们可以快速创建命令行工具,适用于写测试脚本和各种服务的Admin CLI。文章还通过一个简单的demo演示了Cobra的使用方法。 ... [详细]
  • 本文讨论了在openwrt-17.01版本中,mt7628设备上初始化启动时eth0的mac地址总是随机生成的问题。每次随机生成的eth0的mac地址都会写到/sys/class/net/eth0/address目录下,而openwrt-17.01原版的SDK会根据随机生成的eth0的mac地址再生成eth0.1、eth0.2等,生成后的mac地址会保存在/etc/config/network下。 ... [详细]
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
  • JDK源码学习之HashTable(附带面试题)的学习笔记
    本文介绍了JDK源码学习之HashTable(附带面试题)的学习笔记,包括HashTable的定义、数据类型、与HashMap的关系和区别。文章提供了干货,并附带了其他相关主题的学习笔记。 ... [详细]
  • Android自定义控件绘图篇之Paint函数大汇总
    本文介绍了Android自定义控件绘图篇中的Paint函数大汇总,包括重置画笔、设置颜色、设置透明度、设置样式、设置宽度、设置抗锯齿等功能。通过学习这些函数,可以更好地掌握Paint的用法。 ... [详细]
  • Android日历提醒软件开源项目分享及使用教程
    本文介绍了一款名为Android日历提醒软件的开源项目,作者分享了该项目的代码和使用教程,并提供了GitHub项目地址。文章详细介绍了该软件的主界面风格、日程信息的分类查看功能,以及添加日程提醒和查看详情的界面。同时,作者还提醒了读者在使用过程中可能遇到的Android6.0权限问题,并提供了解决方法。 ... [详细]
author-avatar
living_ren
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有