1. 第1章 组件
    1. 1.1 使用组件的优点
      1. 1.1.1 应用程序定制
        1. 一个应用程序可以由多个组件组成,而一个组件可以被多个应用程序使用
      2. 1.1.2 组件库
        1. 基础的组件可以放入组件库,在需要使用的时候拿过来用很方便
      3. 1.1.3 分布式组件
        1. 组件可以放在远程主机上调用
    2. 1.2 对组件的需求
      1. 1.2.1 动态链接
        1. 可以在运行时改变组件
      2. 1.2.2 信息封装
        1. 组件将内部的实现方法和细节封装,用户通过接口调用
        2. 语言无关
          1. 组件可以被多种语言调用
        3. 版本
          1. 一个组件可以有多个版本同时存在
    3. 1.3 COM
      1. 1.3.1 COM组件是
        1. 一个标准
        2. 以二进制的形式发布
        3. 以DLL或EXE的形式存在
      2. 1.3.2 COM组件不是
        1. 不是一种编程语言
        2. 不是DLL
        3. 不是函数集
      3. 1.3.3 COM库
        1. 系统提供的方便开发的基础组件库
      4. 1.3.4 COM方法
        1. 一种编程风格,一种软件组织方法
      5. 1.3.5 COM超越了用户的需求
        1. 客户与组件强制性的隔离
    4. COM提供了编写组件的一个标准方法。遵循COM标准的组件可以被组合起来形成应用程序。每个COM组件均可同其他组件一起使用。实现这种课动态改变组件的关键是信息封装。对于封装,COM通过组件和客户之间的连接或接口来实现。
  2. 第2章 接口
    1. 2.1 接口的作用
      1. 2.1.1 可复用应用程序架构
        1. 接口相当于程序的结构骨干
        2. 只要接口不变,组件可以任意替换
        3. 航天飞机对接
      2. 2.1.2 COM接口的其他优点
        1. 保护系统免受外界变化的影响
    2. 2.2 COM接口的实现
      1. 2.2.1 编码约定
        1. objbase.h
          1. #define interface struct
      2. 2.2.2 一个完整的例子
        1. COM接口在C++中是用纯抽象基类实现
        2. 一个COM组件可以提供多个接口
        3. 一个C++类可以使用多继承实现一个可以提供多个接口的组件
        4. 默认使用__stdcall标准调用约定
      3. 2.2.3 非接口通信
        1. 使用指向CA的指针违背了只使用接口通信
        2. new和delete操作可以控制组件的生命周期,也违背了
      4. 2.2.4 实现细节
        1. 类并非组件
        2. 接口并非总是继承的
        3. 多重接口及多重继承
        4. 命名冲突
    3. 2.3 接口理论:第二部分
      1. 2.3.1 接口的不变性
        1. 一般不会修改已有接口而是加入新的接口
      2. 2.3.2 多态
        1. 按同一种方式处理不同的对象
        2. 接口的行为越小,越可能被复用
        3. 飞行接口比直升机接口更可能复用
    4. 2.4 接口的背后
      1. 2.4.1 虚拟函数表
        1. 定义纯抽象基类就是定义内存结构
        2. COM接口的内存结构和C++编译器为抽象基类生成的内存结构相同
      2. 2.4.2 vtbl指针及实例数据
        1. vtbl指针在由抽象基类函数指针到函数的过程中增加了一个额外的级别
        2. 内存结构
      3. 2.4.3 多重实例
      4. 2.4.4 不同的类,相同的vtbl
    5. 复用整个应用程序框架的能力需要精心设计接口
  3. 第3章 QueryInterface函数
    1. 3.1 接口查询
      1. 3.1.1 关于IUnknown
        1. 所有COM接口都需要继承
      2. 3.1.2 IUnknown指针的获取
        1. 非正式:CreateInstance()
      3. 3.1.3 关于QueryInterface
        1. 接口不支持时返回NULL
      4. 3.1.4 QueryInterface的使用
        1. 通过每个接口唯一的ID查询
      5. 3.1.5 QueryInterface的实现
        1. 非虚拟继承
      6. 3.1.6 关于类型转换
        1. 将this指针进行类型转换将会导致其值得改变;不同的vtbl指针
      7. 3.1.7 一个完整的例子
        1. uuid.lib中获取IID_IUnknown的定义
    2. 3.2 关于QueryInterface的实现规则
      1. 3.2.1 同一IUnknown
        1. 组件的实例只有一个IUnknown
      2. 3.2.2 客户可以获取曾经得到过的接口
      3. 3.2.3 可以再次获取已经拥有的接口
      4. 3.2.4 客户可以从任何接口返回到起始接口
        1. 如果可以通过IX查询到IY,那么反过来也可以;说明这个实例同时实现了这两个接口;类比交换律
      5. 3.2.5 若能够从某接口获取某特定接口,则从任意接口都将能够获取此接口
        1. 调用的都是同一个QueryInterface
    3. 3.3 QueryInterface定义了组件
      1. 3.3.1 接口集
        1. 用户无法得知COM组件到底支持哪些接口,必须通过查询
    4. 3.4 新版本组件的处理
      1. 3.4.1 何时需要建立一个新版本
        1. 接口中函数的数目
        2. 接口中函数的顺序
        3. 某个函数的参数
        4. 某个函数参数的顺序
        5. 某个函数参数的类型
        6. 函数可能的返回值
        7. 函数返回值的类型
        8. 函数参数的意义
        9. 接口中函数的意义
      2. 3.4.2 不同版本接口的命名
        1. 接口名字后面加数字
      3. 3.4.3 隐含合约
        1. 组件对接口的实现改变,影响了客户的调用方法;比如调用顺序改变
          1. 使接口不论怎么调用都正常
          2. 在文档中对调用方法进行说明
      4. 有一项改变了就需要重新定义新的接口
  4. 第4章 引用计数
    1. C++类的头文件表示了这个类所提供的服务及函数
    2. COM组件则不同,更具隐蔽性,客户无法得知全部细节
    3. 4.1 生命期控制
      1. AddRef
      2. Release
    4. 4.2 引用计数简介
      1. 三条规则
        1. 在返回之前调用AddRef。如QueryInterface和Createinstance
        2. 使用完接口之后调用Release
        3. 在赋值之后调用AddRef
      2. 4.2.1 引用计数接口
        1. 对每个接口都要维护引用计数而不是针对整个组件
        2. 1. 方便程序调试
        3. 2. 资源按需获取
          1. 实现某个接口可能需要大量内存和资源
      3. 4.2.2 AddRef和Release的实现
        1. InterlockedIncrement
    5. 4.3 何时进行引用计数
      1. 4.3.1 引用计数的优化
        1. 如果接口IX的生命周期别接口IY的生命周期覆盖,则可以省去引用计数
        2. 局部变量的接口指针不需要引用计数
      2. 4.3.2 引用计数六规则
        1. 输出参数必须加AddRef
        2. 输入参数无需加AddRef/Release
        3. 输入-输出参数规则
          1. 对输入的接口Release,对输出的接口AddRef
        4. 局部变量规则
          1. 无需调
        5. 全局变量规则
          1. 全局变量或者类成员的接口指针,在传递给函数之前必须AddRef
        6. 不能确定时的规则
          1. 都加
  5. 第5章 动态链接
    1. 5.1 组件的创建
      1. 5.1.1 从DLL中输出函数
        1. extern"C" 可以防止C++编译器在函数名称上加类型信息
        2. dumpbin -exports 1.dll
        3. DEF文件
      2. 5.1.2 DLL的加载
        1. LoadLibrary
        2. GetProcAddress
        3. DLL驻留在进程空间
        4. 当客户查询组件的某个接口时,它请求的实际上是具有特定格式的一块内存
    2. 5.2 客户和组件的划分
      1. 5.2.1 程序清单
    3. 5.3 对象串
    4. 5.4 本章小结
  6. 第6章 关于HRESULT、GUID、注册表及其他细节
    1. 6.1 HRESULT
      1. 6.1.1 HRESULT值得查找
        1. FormatMessage
      2. 6.1.2 HRESULT值得使用
        1. 使用SUCCEED和FAILED宏判断
      3. 6.1.3 用户自己代码的定义
        1. 避免自定义错误码
    2. 6.2 GUID
      1. 6.2.1 为什么要使用GUID
        1. 48位网卡地址
        2. 60位时间戳
          1. 自1852年10月15日0点开始的以100纳秒计
      2. 6.2.2 GUID的声明和定义
        1. DEFINE_GUID宏
          1. 需包含initguid.h
      3. 6.2.3 GUID的比较
        1. memcmp
      4. 6.2.4 将GUID作为组件标识符
        1. CLSID
      5. 6.2.5 通过引用传递GUID值
    3. 6.3 Windows注册表
      1. 6.3.1 注册表的组织
        1. 树状
      2. 6.3.2 注册表编辑器
        1. regedit
      3. 6.3.3 CLSID关键字结构
      4. 6.3.4 关于注册表的其他细节
      5. 6.3.5 ProgID
        1. 程序员个某个CLSID指定的一个程序员易记的名字
        2. CLSIDFromProgID
      6. 6.3.6 自注册
        1. DllRegisterServer
          1. regsvr32.exe
          2. 安装程序通过LoadLibrary调用
      7. 6.3.7 组件类别?
      8. 6.3.8 OleView
        1. 查看注册表的高级视图
    4. 6.4 COM库函数
      1. 6.4.1 COM库的初始化
        1. CoInitialize/CoUninitialize
          1. 一个进程只需一次;成对;一般在exe中
      2. 6.4.2 内存管理
        1. CoTaskMemAlloc/CoTaskMemFree
      3. 6.4.3 将字符串转化成GUID
        1. StringFromGUID2
    5. 6.5 本章小结
  7. 第7章 类厂
    1. 7.1 CoCreateInstance
      1. 7.1.1 CoCreateInstance的声明
      2. 7.1.2 CoCreateInstance的使用
      3. 7.1.3 类上下文
        1. CLSCTX_INPROC_SERVER
          1. 在本地同一进程
        2. CLSCTX_INPROC_HANDLER
          1. 进程中处理器,实现组件一部分;其他部分可以在本地或者远程
        3. CLSCTX_LOCAL_SERVER
          1. 在本地另一进程
        4. CLSCTX_REMOTE_SERVER
          1. 在远程
      4. 7.1.4 客户程序清单
      5. 7.1.5 CoCreateInstance的不灵活性
    2. 7.2 类厂
      1. 7.2.1 CoGetClassObject
        1. 返回指向类厂的指针
      2. 7.2.2 IClassFactory
        1. CreateInstance
      3. 7.2.3 CoCreateInstance与CoGetClassObject的比较
        1. CoCreateInstance调用CoGetClassObject
      4. 7.2.4类厂的若干特性
        1. 一个类厂实例创建一个组件
        2. 由开发者实现
    3. 7.3 类厂的实现
      1. 7.3.1 DllGetClassObject的使用
      2. 7.3.2 组件的创建过程
      3. 7.3.3 组件代码清单
      4. 7.3.4 流程控制
      5. 7.3.5 组件的注册
    4. 7.4 同一DLL中的多个组件
      1. 7.4.1 类厂实现的复用
    5. 7.5 DLL的卸载
      1. 7.5.1 DllCanUnloadNow的使用
      2. 7.5.2 LockServer
  8. 第8章 组件复用:包容与聚合
    1. 8.1 包容和聚合
      1. 8.1.1 包容简介
        1. 组件A接口的内部实现要用到另一个组件B
      2. 8.1.2 聚合简介
        1. 包容的特例,一个组件提供多个接口
      3. 8.1.3 包容与聚合的比较
        1. 包容是新手
        2. 聚合是熟练工
    2. 8.2 包容的实现
      1. 8.2.1 接口扩展
    3. 8.3 聚合的实现
      1. 8.3.1 QueryInterface的实现
        1. 不需要集成IY接口
      2. 8.3.2 不正确的IUnknown
        1. 违反了QueryInterface的规则
      3. 8.3.3 聚合的未知接口
        1. CoCreateInstance的pUnknownOuter参数传递外部接口的IUnknown接口
      4. 8.3.4 内部组件的创建
      5. 8.3.5 外部组件中指向内部组件接口的指针
    4. 8.4 一个完整的例子
      1. 8.4.1 盲目聚合
        1. 元接口
    5. 8.5 现实世界中的聚合和包容
      1. 8.5.1 组件的内部状态信息
      2. 8.5.2 虚拟函数的模拟
  9. 第9章编程工作的简化
    1. 9.1 客户端的简化
      1. 9.1.1 智能指针接口
        1. CComPtr
      2. 9.1.2 C++包装类
    2. 9.2 服务器端的简化
      1. 9.2.1 未知接口基类
        1. 从CUnknown基类继承
      2. 9.2.2 类厂基类
        1. CFactory
      3. 9.2.3 CUnknown和CFactory的使用
      4. 9.2.4 集成步骤
        1. 1. 编写实现组件的类
          1. 可以从CUnknown或其他从CUknown派生的类派生出带实现的组件
          2. 使用DECLARE_IUNKNOWN宏来实现代理未知接口
          3. 在组件的构造函数中初始化CUnknown
          4. 实现NodelegatingQueryInterface,在其中加入此组件支持而其基类不支持的接口。对于那些组件所不支持的接口,可以调用相应的基类
          5. 若需要在构造了组件之后进行其他一些初始化处理,可以重载Init函数,如此时可建立被包容或者被聚合的组件
          6. 若需要在组件被删除之前进行其他一些清理工作,可重载FinalRelease函数,如此时可以释放那些指向被包容或聚合的组件的指针
          7. 给组件实现一个静态的CreateInstance函数
          8. 实现组件支持的那些接口
        2. 2. 对于待放到同一DLL中的其他组件,重复上面的步骤1
        3. 3. 编写类厂
          1. 建立一个文件,以包含全局CFactoryData数组g_FactoryDataArray
          2. 定义g_FactoryDataArray数组并用DLL中提供的所有组件的信息填充此组件
          3. 定义变量g_cFactoryDataEntries,其中包含数组g_FactoryDataArray中组件的个数
        4. 4. 编写一个定义DLL入口点的DEF文件
        5. 5. 将上面所编写的代码同CUNKNOWN.cpp和CFACTORY.cpp一起编译链接
    3. 9.3 本章小结
  10. 第10章 EXE中的服务器
    1. 10.1 不同的进程
      1. 10.1.1 本地过程调用(LPC)
        1. 它是基于远程过程调用RPC的用于单机上进程间通信的专利技术
      2. 10.1.2 调整
        1. 数据从一个进程复制到另一个进程
          1. 指针的复制尤其复杂
        2. 实现IMarshal接口
          1. 涉及性能优化
      3. 10.1.3 代理/残根DLL
        1. 几乎每次调用Win32函数都用到了LPC
          1. 这种隔离可以避免对系统造成破坏
        2. 代理DLL
          1. 为客户完成参数的调整及LPC调用
        3. 残根DLL
          1. 对从客户传来的数据进行反调整/将传回客户的数据进行调整
    2. 10.2 IDL/MIDL简介
      1. 10.2.1 关于IDL
        1. 语法同C/C++相似
      2. 10.2.2 IDL接口描述举例
      3. 10.2.3 MIDL编译器
        1. 编译生成头文件和C文件,代理和残根DLL
    3. 10.3 本地服务器的实现
      1. 10.3.1 示例程序的运行
      2. 10.3.2 去掉入口点函数
      3. 10.3.3 类厂的启动
      4. 10.3.4 对LockServer的修改
    4. 10.4 远程访问能力
      1. 10.4.1 DCOMCNFG.EXE所完成的工作
      2. 10.4.2 工作机理
      3. 10.4.3 其他DCOM信息
  11. 第11章 调度接口与自动化
    1. 11.1 一种新的通信方式
      1. 11.1.1 旧的通信方式
        1. 客户与组件之间的通信通过接口完成
      2. 11.1.2 IDispatch接口
        1. 接收一个函数名称并执行它
        2. GetIDsOfNames
          1. DISPID
        3. Invoke
        4. 调度接口
          1. 双重接口
    2. 11.2 IDispatch的使用
      1. 11.2.1 Invoke函数的参数
      2. 11.2.2 示例
      3. 11.2.3 VARIANT类型
        1. 为了摆脱静态类型检查
      4. 11.2.4 BSTR数据类型
        1. 带有字符计数值得字符串
        2. SysAllocString
        3. SysFreeString
      5. 11.2.5 SAFEARRAY类型
        1. 包含边界信息的数组
    3. 11.3 类型库
      1. 11.3.1 类型库的创建
        1. IDL中library语句
        2. 类型库的分发
          1. 可以包在EXE或者DLL中
          2. 可以单独文件发行
      2. 11.3.2 类型库的使用
        1. LoadTypeLib
          1. ITypeLib::GetTypeInfoOfGuid
      3. 11.3.3 注册表中的类型库
    4. 11.4 IDispatch接口的实现
      1. 11.4.1 异常的引发
      2. 11.4.2 参数调整
  12. 第12章 多线程
    1. 12.1 COM线程模型
      1. 12.1.1 Win32线程
      2. 12.1.2 COM线程
        1. 与Win32线程相同
      3. 12.1.3 套间(apartment)
        1. 一个由用户界面风格的线程(套间线程)和一个消息循环构成的概念性实体
        2. 套间通信
      4. 12.1.4 套间线程
        1. 套间线程拥有它所创建的组件,一个套间中的组件只能由相应的套间线程调用
        2. COM将保证所有对组件的调用均被同步
        3. 编程简单,不需要考虑同步
        4. 需要消息循环
      5. 12.1.5 自由线程
        1. 自由线程创建的组件,任何线程都可调用
        2. 开发者应保证线程安全
        3. 不需要消息循环
      6. 12.1.6 调整与同步
        1. 进程间的调用将被调整
        2. 同一线程中的调用将不被调整
        3. 对于套间线程中组件的调用将被调整
        4. 对于自由线程中组件的调用并不总是被调整
        5. 对于套间线程的调用将被同步
        6. 对于自由线程的调用将不被同步
        7. 同一线程中的调用将由此线程本身完成同步
    2. 12.2 套间线程的实现
      1. COM使用一个隐藏的Windows消息队列来同步这种调用
      2. DLL入口点必须是类型安全的
      3. 类厂可能需要是线程安全的
      4. 12.2.1 自动调整
      5. 12.2.2 手工调整
        1. 跨越套间边界但并没有通过COM通信时
        2. CoMarshalInterface
        3. CoMarshalInterTheadInterfaceInStream
      6. 12.2.3 编码
      7. 12.2.4 对套间线程例子的说明
    3. 12.3 自由线程的实现
      1. 12.3.1 对自由线程例子的说明
      2. 12.3.2 自由线程参数调整得优化
        1. 套间线程访问自由线程时,避免调整
        2. 调整很慢
        3. CoCreateFreeThreadedMarshaler
    4. 12.4 关于线程模型的注册表关键字
      1. ThreadingModel
        1. Apartment
          1. 套间线程
        2. Free
          1. 自由线程
        3. Both
  13. 第13章 一个完整的例子
    1. 13.1 Tangram程序
      1. 13.1.1 Tangram的运行
      2. 13.1.2 所用的组件
      3. 13.1.3 客户程序
      4. 13.1.4 TangramModel组件
      5. 13.1.5 TangramGdiVisual和TangramGLVisual组件
      6. 13.1.6 TangramGidWorld和TangramGLWorld组件
    2. 13.2 展示
    3. 13.3 IDL文件
      1. 13.3.1 DLLDATA.C文件
    4. 13.4循环引用计数
      1. 13.4.1 不调用AddRef
      2. 13.4.2 使用显示终止
      3. 13.4.3 使用一个单独的组件
    5. 13.5 事件和连接点
      1. 13.5.1 IEnumXXX
    6. 13.6 本章小结
  14. 接口可以通过封装其内部实现细节使一个由组件构成的系统免受变化得影响。只要接口不变,组件可以随便替换。C++编译器为纯抽象基类生成的内存结构同COM接口的要求相同。
  15. COM组件大部分的灵活性及其封装能力都由QueryInterface提供
  16. IUnknown的成员函数可以使程序员对接口进行完全控制。AddRef是说我们要使用某个接口,而Release是说我们用完了某个接口。这就是生命周期的控制。
  17. COM的几个细节
  18. 大多数情况下可以用CoCreateInstance来创建组件;类厂是创建组件的组件
  19. 通过组件的包容和聚合实现组件的复用、扩展和定制;COM不支持继承
  20. 智能接口指针;CUnknown和CFactory
  21. 跨越进程边界
  22. 另一种组件和客户之间通信的方法。若组件只被编译型语言如C或C++访问,可以使用vtbl或常规的COM接口;若组件将被VB或Java访问,则应该实现一个双重接口。
  23. 套间是由一个线程和一个消息循环构成的概念性实体;套间线程必须初始化COM,必须具有一个消息循环,必须在指针跨越套间时进行调整;自由线程必须使用CoInitializeEx初始化,对性能有要求时使用