《COM原理与应用》笔记

  从技术上讲,接口是包含了一组函数的数据结构,客户程序用一个指向接口数据结构的指针来调用接口成员函数。如图2.2所示,接口指针实际指向另一个指针,第二个指针指向一组函数,称为接口函数表,接口函数表中每一项为4个字节长的函数指针,每个函数指针与对象的具体实现连接起来。通过这种方式,客户程序只要获得接口指针,就可以调用对象的实际功能。

  接口函数表通常称为虚函数表(virtual function table,简称vtable),指向vtable的指针为pVtable。
  对于一个接口来说,它的虚函数表vtable是确定的,因此接口的成员函数个数是不变的,而且成员函数的先后顺序也是不变的;对于每个成员函数来说,其参数和返回值也是确定的。在一个接口的定义中,所有这些信息都必须在二进制一级确定,不管什么语言,只要能支持这样的内存结构描述,就可以定义接口。
  用C语言描述字典接口
  struct IDictionaryVtbl;
  struct IDictionary
  {
    IDictionaryVtbl *pVtbl;
  };
  struct IDictionaryVtbl
  {
    BOOL (*Initialize)(IDictionary *this);
    BOOL (*LoadLibrary)(IDictionary *this, String);
    BOOL (*InsertWord)(IDictionary *this, String, String);
    void (*DeleteWord)(IDictionary *this, String);
    BOOL (*LookupWord)(IDictionary *this, String, String *);
    BOOL (*RestoreLibrary)(IDictionary *this, String);
    void (*FreeLibrary)(IDictionary *this);
  };
  (1)每个接口成员函数的第一个参数为指向IDictionary的指针,这是因为接口本身并不独立使用,它必定存在于某个COM对象上,因此,该指针可以提供对象实例的属性信息,在被调用时,接口可以知道是对哪个COM对象在进行操作。所以,该this指针与C++类成员函数定义中隐藏的this指针非常类似。如果我们在一个应用系统中同时用到了两本字典,即存在两个字典对象,不同的字典对象其this指针不同。
  (2)在接口成员函数中,字符串变量必须用Unicode字符指针,COM规范要求使用Unicode字符,而且COM库中提供的COM API函数也使用Unicode字符。所以,如果在组件程序内部用到了ANSI字符的话,应该对两种字符进行转换,操作系统或者C/C++编译库会提供这样的转换函数。
  (3)不仅成员函数的参数类型是确定的,而且应该使用同样的函数调用方式。客户程序在调用成员函数之前,必须先把参数压入栈中,然后再进入成员函数,成员函数依次把参数从栈中取出,在函数返回之前或返回之后,必须恢复栈的当前位置,才能保证程序的正常运行。在Windows平台上有两种调用方式,分别为_cdecl和_stdcall(在有的编译器中称为pascal),采用_cdecl可以实现C语言中用到的函数可变参数的特性(例如printf函数),在这种调用方式下,由调用程序处理栈的恢复。由于大多数语言(除C/C++之外)都使用了_stdcall或pascal,而且大多数的系统API(支持可变参数的函数例外)也都使用这种调用方式,所以,COM规范也采用_stdcall和pascal,并且,所有的COM API函数也使用了_stdcall。当然,这不是绝对的,但必须保证调用方和被调用方使用一致的调用方式,如果接口成员函数使用了_cdecl,则C/C++之外的大多数语言就不能使用这样的接口,所以,除非要使用可变参数特性,否则就使用_stdcall。
  (4)在C语言中,用这种结构只是描述了接口,并没有提供具体的实现,对于客户程序,它只需要这样的描述就可以调用COM对象的接口;而对于组件程序,基于这样的描述必须提供具体的实现过程,也就是说,如果一个COM对象实现了这个接口,则它所提供的接口指针IDictionary所指向的IDictionaryVtbl结构中,每个成员必须是有效的函数指针。
  (5)从C语言的描述中我们可以看出,由于COM接口的这种二进制结构,只要一种编程语言能够支持“structure”或“record”类型,并且这种类型能够包含双重的指向函数指针表的成员,则它就可以支持接口的描述,从而可以用于编写COM组件或者使用COM组件。

---------------------------------------- 分割线 ----------------------------------------

  COM接口也采用了全局唯一标识符,它被称为接口标识符(IID,interface identifier)。例如:
  extern "C" const IID IID_IUnknown =
  { 0x00000000, 0x0000, 0x0000,
  { 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 }};
  客户程序要使用接口,必须知道该接口的IID和接口提供的方法(接口成员函数)。

---------------------------------------- 分割线 ----------------------------------------

COM接口结构中的vtable与class的vtable(类的虚函数表)完全一致,因此,用class描述COM接口是最方便的手段。
  用C++类重新定义IDictionary:
  class IDictionary
  {
    virtual BOOL Initialize() = 0;
    virtual BOOL LoadLibrary(String) = 0;
    virtual BOOL InsertWord(String, String) = 0;
    virtual void DeleteWord(String) = 0;
    virtual BOOL LookupWord(String, String *) = 0;
    virtual BOOL RestoreLibrary(String) = 0;
    virtual void FreeLibrary() = 0;
  };
  class定义中隐藏了虚函数表vtable,每个成员函数隐藏了第一个参数this指针,this指针指向类的实例。图2.3显示了类IDictionary的内存结构:

  类IDictionary使用了纯虚函数,因为接口只是一种描述,并不提供具体的实现过程。如果COM对象要实现接口IDictionary,则COM对象必须以某种方式把它自身与类IDictionary联系起来,然后将IDictionary的指针暴露给客户程序。
  当客户程序获得了COM对象的接口指针pIDictionary之后,就可以调用接口的成员函数,例如:
  pIDictionary->LoadLibrary("Eng_Ch.dict");
  如果使用C语言的struct IDictionary,则应该这样:
  pIDictionary->pVtbl->LoadLibrary(pIDictionary, "Eng_Ch.dict");
  由C++语言class的特性可知上述两种调用完全等价。

---------------------------------------- 分割线 ----------------------------------------

  用IDL描述接口IDictionary
  interface IDictionary
  {
    HRESULT Initialize();
    HRESULT LoadLibrary([in]string);
    HRESULT InsertWord([in]string, [in]string);
    HRESULT DeleteWord([in]string);
    HRESULT LookupWord([in]string, [out]string *);
    HRESULT RestoreLibrary([in]string);
    HRESULT FreeLibrary();
  };

COM接口定义和标识相关推荐

  1. 我的编码习惯 —— API 接口定义

    工作中,少不了要定义各种接口,系统集成要定义接口,前后台掉调用也要定义接口.接口定义一定程度上能反应程序员的编程功底.列举一下工作中我发现大家容易出现的问题: 1. 返回格式不统一 同一个接口,有时候 ...

  2. usb接口定义引脚说明_PerfDogService使用说明

    令牌申请教程:https://bbs.perfdog.qq.com/article-detail.html?id=55安装包下载:https://perfdog.qq.com/sdk一. 概述 Per ...

  3. ADAS自动驾驶辅助系统通讯协议「ADASIS Protocol」接口定义解析(二)详细

    「ADASIS Protocol Introduction」 --信号接口解析 本文来自微信公众号:[阿波兹得][续]ADASIS Protocol介绍 (2) 信号解析https://mp.weix ...

  4. 接口定义语言IDL,COM

    接口定义语言MIDL:Microsoft Interface Definition Language. MIDL是定义COM接口的说明性语言.是一种独立于语言的接口定义方式,从而产生RPC(Remot ...

  5. SAP工具箱 自动生成发出接口程序(二 接口定义表)

    点击蓝字 关注我们 一 前言 前文介绍了发出接口程序的执行原理及怎么自动生成发出接口程序. 详见链接 无峰,公众号:ABAP 技巧与实战SAP工具箱 自动生成发出接口程序(一) 本文主要介绍生成发出接 ...

  6. 接口定义,常见的接口,常见的接口请求方式,put请求和patch请求的区别

    接口定义:应用程序编程接口,指前后端数据交互的一套标准,包括软件内部之间的接口,硬件之间的接口或者是软件对外的接口. 常见的接口类型:         1,Webservice接口,主要用于服务端的接 ...

  7. Thrift的接口定义语言IDL

    Thrift的IDL可以使用下面的语法来定义描述接口. 1 基本类型 bool:布尔值,true 或 false byte:8 位有符号整数 i16:16 位有符号整数 i32:32 位有符号整数 i ...

  8. 【C 语言】字符串模型 ( strstr-while 模型 | 抽象函数模型 | 业务子函数接口定义要点 | 形参指针间接赋值 | 返回值状态 | 形参指针处理 | 形参指针判空 | 形参返回值 )

    文章目录 前言 一.业务子函数接口定义要点 二.完整代码示例 前言 字符串开发模型 : strstr-while/do-while 模型 : 在 字符串 中 查找 子串特征 ; 两头堵模型 : 两个指 ...

  9. JDK中提供的实现——通过 java.util.Observable 类和 java.util.Observer 接口定义了观察者模式,只要实现它们的子类就可以编写观察者模式实例

    JDK中提供的实现 在 Java 中,通过 java.util.Observable 类和 java.util.Observer 接口定义了观察者模式,只要实现它们的子类就可以编写观察者模式实例. 1 ...

最新文章

  1. pgsql 前10条_白沙湾南片区11条新建道路最新进度及建成时间,已建成一条!还有一条将通车...
  2. mysql之慢查询详解,mysqldumpslow的使用
  3. 天津科技大学计算机基础,天津科技大学大学计算机基础样卷
  4. matla工具箱 SerialLink 的一些最近发现的功能
  5. 随想录(人脸检测之dlib)
  6. android高德地图自动缩放比例,【Android】高德地图 缩放级别及像素以及地图上的点转化成屏幕上的点...
  7. 世界读书日,给你们送大福利!
  8. 计算机组成原理华中科技大学秦磊华,计算机组成原理(华科)chap1
  9. win10 android10之后高通芯片 adb和fastboot驱动无法识别问题
  10. 主机安全加固终端安全管理
  11. java web实现markdown_editormd实现Markdown编辑器写文章功能
  12. pdf在线免费去水印 以及图片去水印 方法
  13. 【Gazebo入门教程】第四讲 场景建模/建筑编辑器
  14. 同步BUCK死区时间产生电路原理及仿真
  15. 从零开始开发一个全栈Web应用实录
  16. Unity3d 中创建实时视频聊天
  17. 云主机和电脑主机服务器有什么区别?
  18. error C2448 函数样式初始值设定项类似函数定义
  19. bzoj1758 [Wc2010]重建计划
  20. 星志远电商:拼多多头像如何保存?

热门文章

  1. IDEA反编译出整个jar包源码
  2. Android面试问答题
  3. NS版块可下载资源综合(updated 2007.12.12)--百思论坛
  4. 【网络研讨会】MongoDB Vs 效仿者:选择MongoDB的理由
  5. (贪心5.3.2)POJ 1505 Copying Books()
  6. Linux 压缩解压命令
  7. @media媒体查询——详解
  8. python语法与函数
  9. Oracle 数据库入门之----------------------多表查询
  10. 小世界网络邻接矩阵生成——python