COM组件初探——《Inside COM》笔记

  一个应用程序通常是由单个的二进制文件组成的,这样的话,每次更新其中的一部分都需要将整个应用程序重新编译。而 COM 的基本思想就是:将单个的应用程序分隔成多个独立的部分,也即组件。这并不是说将应用程序分割成文件、模板或类,因为这在本质上并没有什么变化,仍然是将它们编译并链接成一个铁板一块状的应用程序。在组件架构中,一个组件类似于一个微型应用程序(即:已经编译、链接好并可以使用),而应用程序是由这样的多个组件打包而成的(各定制的组件在运行时同其他组件连接起来以构成某个应用程序)。这样的架构思想实现了修改应用程序只需替换其中一个组件,而无须陷入整体重新编译的窘态。

COM 有几个特性——

  • 它并不是类似 Win32 API 那样的函数集,它只是一个规范,提供了编写组件的一个标准方法,而遵循 COM 标准的组件可以被组合起来以形成应用程序;
  • 它不是一种计算机语言,恰恰相反,因为组件是以二进制形式发布的,它具有与语言的无关性,可用任何语言来编写组件,且任一客户能够使用任一组件;
  • COM 组件是以 Win32 动态链接库(DLLs)或可执行文件(EXEs)的形式发布的可执行代码组成的,但它本身与 DLL 并没有很大关系,只不过 COM 利用了 DLL 极佳的动态链接能力。

  COM 是与语言无关的,所以为了将客户与 COM 组件连接起来,COM 组件通过接口与外界打交道。而对于 COM 组件这样的二进制文件来说,什么是接口呢?它同样有着一个二进制的标准——表示一个接口的内存块必须具有一定的结构。

  以下将用 C++ 语言来描述这些细节。

  首先介绍 COM 组件中接口的概念,以下贴一段描述接口实现以及使用的代码,但注意:这并不是真正的 COM 接口,COM 接口还需支持一些额外的属性。

#include <iostream>
#include <objbase.h>    //此头文件中定义有 #define interface struct 

using namespace std;

//定义 IX,IY 接口
interface IX //纯抽象基类
{
    virtual void _stdcall Fx1() = 0; //纯虚函数
    virtual void _stdcall Fx2() = 0;
};
interface IY
{
    virtual void _stdcall Fy1() = 0;
    virtual void _stdcall Fy2() = 0;
};

//实现组件
class CA :public IX, public IY
{
public:
    //实现 IX 接口
    virtual void __stdcall Fx1() { cout << "CA::Fx1" << endl; }
    virtual void __stdcall Fx2() { cout << "CA::Fx2" << endl; }
    //实现 IY 接口
    virtual void __stdcall Fy1() { cout << "CA::Fy1" << endl; }
    virtual void __stdcall Fy2() { cout << "CA::Fy2" << endl; }
};

void trace(const char* pMsg)
{
    cout << pMsg << endl;
}

//客户
int main()
{
    trace("客户:创建该组件的实例");
    CA* pA = new CA;

    //得到指向 IX 的指针
    IX* pIX = pA;

    trace("客户:调用 IX 接口");
    pIX->Fx1();
    pIX->Fx2();

    //得到指向 IY 的指针
    IY* pIY = pA;

    trace("客户:调用 IY 接口");
    pIY->Fy1();
    pIY->Fy2();

    trace("客户:删除组件");
    delete pA;

    cin.get();
    return 0;
}

  如代码所见,在 C++ 中用纯抽象基类来定义 COM 接口,这是因为纯抽象基类所定义的内存结构可以满足 COM 对接口的要求。

  那么纯抽象基类所定义的内存结构是什么样的呢?这就不得不提到虚函数表了。

  我们知道,在 C++ 中,因为虚函数的存在,导致了隐式向上强制转换的问题(即:基类指针或引用可以指向基类对象或派生类对象),为了能够在程序运行时选择正确的虚方法(究竟是基类的还是派生类的,该过程又被称为动态联编),编译器采用了虚函数表的方法——在每个类中添加一个隐藏的指针成员,其指向一个函数地址数组,而这个数组中存储了所有定义的虚函数的地址(对于派生类来说,如果其没有重新定义虚函数,数组中将继承基类中对应虚函数原始版本的地址),这个数组就被称为虚函数表(virtual function table, vtbl)。

  那么,纯抽象基类的内存结构也就可想而知了——一块连续的内存中存储了所有的虚函数的地址,而当派生类继承一个抽象基类时,它也将继承此内存结构。而“巧合”的是,COM 接口的内存结构同 C++ 编译器为抽象基类所生成的内存结构是相同的。

  今天将《Inside COM》读到了第二章,做一个阶段性的笔记总结,我这么懒,可能会过几天再往后看吧QAQ

  PS:我对于 C++ 的编译器实现并不清楚,在虚函数表的解释那儿可能有点儿问题,欢迎友好交流=。=

时间: 2024-10-09 21:05:25

COM组件初探——《Inside COM》笔记的相关文章

初探排序学习笔记

简单选择排序 思路:选出最小的元素,放在第一个位置,之后在剩下的元素中,选出最小的元素,放在第二个位置.........以此类推,直到完成排序. package h; public class MyA { static void selectOne(int[] a, int begin) { int p = begin; //假设修正法 for(int i=begin+1; i<a.length; i++){ if(a[i] < a[p]) p = i; //记录最小元素所在位置 } {int

初探单例模式学习笔记

一.如何防止一个类产生多个实例呢? 1.不做任何措施,贴出一幅海报,通知所有程序员不能对这个类创建多个实例  (不现实) 2.让这个类无法创建另一个实例    -> 单例模式 二.但是对类进行实例化,它的决定权在类的外部,如何将决定权回归类的自身呢? -> 将构造函数变成private类型  ,不允许外界直接调用构造方法创建实例 三.但我们总要给外界提供一个途径获得类的实例 class T { private T(); public static T getInstance() { retur

《纵向切入ASP.NET 3.5控件和组件开发技术》笔记:高效率事件集合对象

在之前讲的几个例子中,使用的是最普通的定义事件方法,比如KingTextBox中事件是这样定义的:/// <summary>/// 获得本书更多内容,请看:/// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx/// </summary>public event EventHandler TextChanged;protected virtual void OnTextChanged(EventArgs

java学习:AWT组件和事件处理的笔记(1)--Frame

1.java的抽象窗口工具包(AWT)中包含了许多类来支持GUI设计2.AWT由java的java.awt包提供3.再进行GUI编程时,要理解:容器类(Container),组件(component).4.Button,Scrollbar.Canvas,List,Checkbox,TextField,TextArea,Label类是java.awt包中的类,更是java.awt包中的Component(组件)的子类.5.java把Component类的子类或间接子类创建的对象称为组件.6.jav

关于前端的photoshop初探的学习笔记

写在前边 这还是高三的时候暑假的时候学习这个软件时记的笔记呢,今天又在电脑上找到了它,总觉得不应该让他尘封在我的硬盘上,于是挂了出来.温馨提示:比较乱,写给自己看的,看不下去,按ctrl+W 笔记内容 ps简介可以用于合成.可以三维adobe bridge图像浏览器 可以直接将图片拖动到ps的编辑系统中..但是是出于临时文件状态,还需要对他进行保存..网站上某些图片不能够拉动,但是可以利用截图功能来实现..两张图片同时拉倒一个文件中构成两个不同的图层..打开文件的几种方法.. ps数码相片点阵图

linux下php安装扩展组件(个人工作笔记系列之php)

系统环境:CentOS release 6.2 (Final) 软件版本:php-5.3.13 php安装的编译参数: ./configure --prefix=/usr/local/php5 --with-config-file-path=/usr/local/php5/etc --with-mysql=/usr/local/mysql-5.5.23/ --with-mysqli=mysqlnd --with-pdo-mysql --with-apxs2=/usr/local/apache2/

form自动验证组件(Tinyshop)-笔记

验证对象: Input.textarea 验证内容: 1.   多个输入组件值是否相同(输入密码和重新输入密码是否一致) 2.   模式匹配 3.   Input.textarea组件其他属性(自带和自定义)如maxlength等 验证结果提示显示: 1.   结果小图标("√"和"×")直接显示在输入框同行,包括提示信息 2.   提示信息换行显示 3.   集中显示在form表单的底部(可自行修改) 关键部分: 输入内容是否一致:这个组件使用bind属性绑定相同

和组件进行双向绑定(笔记)

1.通过关键字sync进行绑定 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Comp

[Nodejs]初探nodejs学习笔记- 如何使用nodejs搭建简单的UDP聊天功能

何为UDP(User Datagram Protocol)? 从baidu摘过来一段:UDP,用户数据报协议,与所熟知的TCP(传输控制协议)协议一样,UDP协议直接位于IP(网际协议)协议的顶层.根据OSI(开放系统互连)参考模型,UDP和TCP都属于传输层协议.UDP协议的主要作用是将网络数据流量压缩成数据包的形式.一个典型的数据包就是一个二进制数据的传输单位.每一个数据包的前8个字节用来包含报头信息,剩余字节则用来包含具体的传输数据. UDP报文没有可靠性保证.顺序保证和流量控制字段等,可