首页计算机计算机软件高级软件工程 06 COM基础:接口与实现.pptx

06 COM基础:接口与实现.pptx

页数:54页文件大小:198.4 KB来源: 高级软件工程ppt课件

本课件系统讲解COM基础。综述部分说明COM源于OLE、是客户与构件互操作所遵循的标准,介绍COM构件以DLL或EXE发布、COM库的核心服务以及语言无关性、进程透明性与复用性等特性。接口部分说明接口的内存结构、虚拟函数表、接口特点、IUnknown的三个方法、引用计数规则与QueryInterface的实现规则及版本管理。实现部分依次讲解DLL、HRESULT、GUID与CLSID、Windows注册表、COM库常用函数、类厂与CoCreateInstance、CoGetClassObject,并给出客户方与服务器方的完整C++示例代码。

COM章节封面,标示构件对象模型(Component Object Model)这一基础部分的主题
章节目录页,分基础部分的综述、接口、实现与高级部分的包含聚合、自动化、DCOM列出内容
起源页,说明COM源于OLE、为克服DDE效率低与稳定性差等不足而出现并被OLE2采用
查看本课件的文字版(共 54 页,供检索与复制)

第1页:构件对象模型 COM

构件对象模型:COM(Component Object Model)

第2页:COM 章节内容

内容:

  • 基础部分:综述、接口、实现
  • 高级部分:包含与聚合、COM与VC、自动化、DCOM

第3页:COM 的起源

(1)COM的起源:源于 OLE(Object Link and Embedding)。OLE1 采用 DDE(Dynamic Data Exchange)在不同的程序之间进行通信;DDE 缺点:效率低、稳定性不好、使用不方便。COM 是为克服上述不足而出现的,OLE2 以 COM 为基础,但 OLE 未能体现 COM 优点。

第4页:什么是 COM

(2)什么是COM:构件对象模型(Component Object Model)是客户与构件为了能够互操作而遵循的标准。COM标准包括规范与实现两部分:

  • 规范部分:定义了构件之间的通信机制,这些规范不依赖于任何特定的语言和操作系统
  • 实现部分:即COM库,为COM规范的具体实现提供一些核心服务

第5页:COM 构件

(3)COM构件:

  • 以DLL或EXE形式发布的代码,与语言无关
  • 以二进制形式发布
  • 可以在不妨碍客户的形式下被升级
  • 可以透明地在网络上被重新分配

构件与类:一个构件可以由多个类实现;接口与类:一个类可以实现多个接口。

第6页:COM 库

(4)COM库(COM Library),功能:

  1. 实现客户方与服务器方COM应用的创建过程
  2. COM通过注册表查找本地服务器(即EXE程序)以及程序名与CLSID的转换
  3. 提供标准的内存控制方法

DCOM的实现提供了分布式环境下的通信机制,在操作系统层次以DLL文件的形式存在。

第7页:COM 特性

(5)COM特性:

  • 语言无关性:为跨语言合作开发提供了统一标准,并得到不同集成开发环境的支持
  • 进程透明性:进程内服务程序(DLL)、本地服务程序(EXE)、远地服务程序(DLL或EXE)。实现进程透明性的关键是COM库,它负责服务体的定位、管理对象的创建及对象与客户之间的通信
  • 复用性:包含方式、聚合方式

第8页:COM 发展趋势

(6)COM发展趋势:

  • 操作系统:成为系统的基本软件模型
  • 数据库:OLE DB/ADO 以COM的方式为数据访问提供一致的接口
  • Internet:ActiveX 包含了所有基于COM的Internet相关技术
  • COM+:增加 MTS 等服务

第9页:COM 接口

2 COM接口:COM接口是COM规范的核心内容。一个接口包含了一组函数;在C++中,可以用抽象基类来定义COM接口。接口的实现:vtbl 指针、虚拟函数表。

第10页:接口的内存结构

接口的内存结构:pIX 指向虚拟函数表,表中依次存放 Fx1、Fx2、Fx3、Fx4 的函数指针。

本页以内存结构示意图为主,建议对照原图查看。

第11页:Vtbl 的作用

Vtbl的作用:提高接口实现的灵活性。例子:一个实现接口IX的类CA,CA包含2个数据(Data1、Data2);pA 指向实例,实例中的 vtbl 指针指向虚拟函数表(Fx1~Fx4)。

本页以内存布局示意图为主,建议对照原图查看。

第12页:实例共享虚拟函数表

两个实例共享同一Vtbl:pA1 与 pA2 分别指向各自的 Data1、Data2,但通过各自的 vtbl 指针指向同一份虚拟函数表(Fx1~Fx4)。

本页以内存布局示意图为主,建议对照原图查看。

第13页:接口特点

接口特点:

  • 实现级:二进制,独立于编程语言
  • 稳定性:客户与服务器依赖于接口
  • 继承性:便于扩展接口,不提倡继承(所有接口皆继承 IUnknown)
  • 多态性

第14页:IUnknown 接口

(1)功能提供:生存期控制、接口查询。IUnknown的定义(IDL):

interface IUnkown { virtual HRESULT QueryInterface(const IID&, void **ppv) = 0; virtual ULONG AddRef() = 0; virtual ULONG Release() = 0; }

第15页:所有接口都继承 IUnknown

所有接口都必须继承 IUnknown:接口IX的虚拟函数表最前面是 QueryInterface、AddRef、Release,其后才是 Fx 等接口自身的函数。

本页以虚拟函数表结构示意图为主,建议对照原图查看。

第16页:IUnknown 指针的获取

IUnknown指针的获取:创建构件时,客户可以使用 CreateInstance,而不是 new。

IUnknown * CreateInstance() { IUnknown * pI = static_cast<IX *> (new foo); pI-> AddRef(); return pI; }

第17页:引用计数

(2)引用计数:AddRef 与 Release 控制构件的生命期,解决内存管理问题,使构件能够自己将自己删除。

使用规则:

  • 返回接口指针之前调用 AddRef
  • 使用完接口之后调用 Release
  • 赋值之后调用 AddRef

第18页:引用计数的实现层次

引用计数的实现方式:可以在不同层次上进行计数——构件(构件引用记数)、对象(对象引用记数)、接口(接口引用记数)。

本页以三种计数层次的对比示意图为主:构件级为整个构件共享一个计数,接口级则为每个接口维护独立计数,建议对照原图查看。

第19页:接口查询 QueryInterface

(3)接口查询:一个COM对象(构件)可以实现多个接口,使用 QueryInterface 查询某个构件是否支持某个特定的接口。

void foo(Iunknown * pI) { IX * pIX = NULL; HRESULT hr = pI->QueryInterface(IID_IX, (void **)&pIX); if (SUCCEEDED(hr)) { pIX->Fx(); } }

第20页:QueryInterface 的实现

QueryInterface的实现(假定存在类CA,继承接口IX与IY):

HRESULT __stdcall CA::QueryInterface(const IID& iid, void ** ppv) { if (iid == IID_IUnknown) { *ppv = static_cast<IX *>(this); } else if (iid == IID_IX) { *ppv = static_cast<IX *>(this); } else if (iid == IID_IY) { *ppv = static_cast<IY *>(this); } else { *ppv = NULL; return E_NOINTERFACE; } static_cast<Iunknown *>(*ppv)->AddRef(); return S_OK; }

第21页:QueryInterface 的实现规则

QueryInterface的实现规则:

  • QueryInterface 返回的总是同一个 IUnknown 指针
  • 若客户曾经获取过某个接口,那么它将总能获取此接口
  • 客户可以再次获取已经拥有的接口
  • 客户可以返回到起始接口
  • 若能够从某个接口获取某特定接口,则可以从(该构件的)任意接口获取此接口

第22页:新版本构件的处理

新版本构件的处理:COM接口永远不会变化——一个IID就是一个接口,通过发行新版本构件解决兼容性。当下列条件中的任何一个变化时应给新接口指定新IID:

  • 接口中函数数目
  • 接口中函数顺序
  • 某个函数的参数
  • 某个函数参数的顺序
  • 某个函数参数的类型
  • 函数可能的返回值
  • 函数返回值的类型等

不同版本接口的命名:在老名称后加一个数字。

第23页:DLL 与 HRESULT

3 COM实现(1)预备知识:DLL

  • DLL是一个构件服务器,EXE也可以是构件服务器
  • 使用户在应用程序的运行过程中替换构件
  • 构件是DLL中实现的接口集
  • DLL共享它们所链入的应用程序地址空间

HRESULT:向用户报告构件运行结果状况(“Here is the RESULT”),在 WINERROR.H 中进行定义,用户可以定义自己的代码。

第24页:GUID 与 CLSID

GUID:Globally Unique IDentifier,用于标识构件(CLSID)与接口(IID),128位(长整数);由于空间足够大,接口标识冲突较小,可以用编程的方法生成具有唯一性的GUID(VC:UUIDGEN.EXE、GUIDGEN.EXE)。它借鉴了OSF的UUID(DCE中的RPC使用),对GUID值的传递通常通过引用进行。

CLSID:CLaSs IDentifier。

第25页:Windows 注册表

Windows注册表:Windows操作系统的共享系统数据库,包括硬件、软件配置及用户等各种信息,由 REGEDIT.EXE(95/98)、REGEDIT32.EXE(NT)启动,可以使用 REGSVR32.EXE 来注册某个构件。

注册表是一个由许多元素构成的层次结构,每一个元素均被称作是一个关键字,每一个关键字可以包含一系列子关键字等。例如:HKEY_CLASSES_ROOT、CLSID、ProgID 等。

第26页:注册表结构示意图

本页为Windows注册表结构示意图,源文件仅提取到页面标签“COM基础”,建议对照原图查看。

第27页:构件与注册表关系示意图

本页为COM构件与注册表关系示意图,源文件仅提取到页面标签“COM基础”,建议对照原图查看。

第28页:COM 库概述

(2)COM库:COM构件与客户皆需要完成的相同操作由 OLE32.DLL、OLE32.LIB 实现。COM库中常用的函数:

  • 初始化函数
  • 与GUID相关的函数
  • 对象创建函数
  • 内存管理函数

第29页:COM 库的初始化函数

在使用COM库中的其他函数(除 CoBuildVersion)之前,进程必须先调用:

HRESULT CoInitialize(void * reserved)

在退出之前调用:void CoUninitialize(void * reserved)。其它:CoBuildVersion、CoFreeUnusedLibraries;OleInitialize 建立在 CoInitialize 基础之上,DCOM 使用 CoInitializeEx 初始化。

第30页:与 GUID 相关的函数

与GUID(CLSID)相关的函数:例如将GUID转换为一个字符串:

wchar_t szCLSID[39]; int r = ::StringFromGUID2(CLSID_Component1, szCLSID, 39);

其它:StringFromCLSID、StringFromIID、CLSIDFromString、IIDFromString、IsEqualGUID、IsEqualIID、IsEqualCLSID、CLSIDFromProgID。

第31页:对象创建与内存管理函数

COM库常用函数:

对象创建与内存管理函数
类别函数
对象创建函数CoGetClassObject、CoCreateInstance、CoCreateInstanceEx、CoRegisterClassObject、CoRevokeClassObject、CoDisconnectObject
内存管理函数CoTaskMemAlloc、CoTaskMemRealloc、CoTaskMemFree、CoGetMalloc

第32页:类厂(Class Factory)

(3)类厂(Class Factory):能够创建其他构件的构件(构件厂),其本身也是一个COM对象,支持一个特殊的接口 IClassFactory。每一个COM对象类应该有一个相应的类厂对象。

第33页:CoCreateInstance

CoCreateInstance(COM库函数之一):

HRESULT __stdcall CoCreateInstance( const CLSID& clsid, IUnknown * pIUnknownOuter, DWORD dwClsContext, const IID& iid, void ** ppv);
  • clsid:待创建构件的标识
  • pIUnknownOuter:用于聚合
  • dwClsContext:限定所创建构件的上下文
  • iid:构件上待使用接口的标识
  • ppv:所返回接口的指针

第34页:CoCreateInstance示例

一个使用例子:

IX * pIX = NULL; HRESULT hr = ::CoCreateInstance(CLSID_Component1, NULL, CLSCTX_INPROC_SERVER, IID_IX, (void **) &pIX); if (SUCCEEDED(hr)) { pIX->FX(); pIX->Release(); }

第35页:类厂在对象创建中的作用

类厂:CoCreateInstance 实际上未直接创建COM构件,而是创建了一个被称为类厂的构件;类厂构件的唯一功能是创建其他构件,客户可以通过类厂所支持的接口来对类厂创建构件的过程加以控制。

CoCreateInstance 调用 CoGetClassObject 创建构件,创建构件的标准接口是 IClassFactory。

第36页:CoGetClassObject

CoGetClassObject(创建类厂):

HRESULT __stdcall CoGetClassObject( const CLSID& clsid, DWORD dwClsContext, COSERVERINFO * pServerInfo, const IID& iid, void ** ppv);

第37页:IClassFactory 接口

IClassFactory:用于创建构件的标准接口

Interface IClassFactory : IUnknown { HRESULT __stdcall CreateInstance(IUnknown * pUnknownOuter, const IID & iid, void ** ppv); HRESULT __stdcall LockServer(BOOL block); }

未接收 CLSID:只能创建同某个CLSID相应的构件(扩展见 IClassFactory2)。

第38页:CoCreateInstance 的实现

CoCreateInstance与CoGetClassObject的关系:CoCreateInstance 通过 CoGetClassObject 实现。

HRESULT CoCreateInstance(const CLSID& clsid, IUnknown * pUnknownOuter, DWORD dwClsContext, const IID& iid, void ** ppv) { * ppv = NULL; IClassFactory * pIFactory = NULL; HRESULT hr = CoGetClassObject(clsid, dwClsContext, NULL, IID_IClassFactory, (void **) &pIFactory); if (SUCCEEDED(hr)) { hr = pIFactory->CreateInstance(pUnknownOuter, iid, ppv); pIFactory->Release(); } return hr; }

第39页:何时用CoGetClassObject

在下列情况下应直接使用 CoGetClassObject 而不使用 CoCreateInstance:

  1. 如果希望用不同于 IClassFactory 的某个创建接口来创建构件,例如 IClassFactory2
  2. 如果希望创建同一构件的多个实例

第40页:类厂的实现

(4)类厂的实现:CoGetClassObject 需要DLL中的一个特定的函数来创建构件的类厂:

STDAPI DllGetClassObject(const CLSID& clsid, const IID& iid, void ** ppv);

第41页:创建与调用构件的全过程

创建、调用构件的过程:客户调用 COM库 的 CoGetClassObject → COM库调用 DLL 的 DllGetClassObject 创建类厂 → 返回 IClassFactory 指针 → 客户调用 IClassFactory::CreateInstance 创建构件 → 返回 IX 接口指针 → 调用 IX::Fx。

第42页:客户方代码示例

(5)例子——客户方代码:

#include <iostream.h> #include <objbase.h> #include "Iface.h" int main() { CoInitialize(NULL); IX* pIX = NULL; HRESULT hr = ::CoCreateInstance(CLSID_Component1, NULL, CLSCTX_INPROC_SERVER, IID_IX, (void**)&pIX); if (SUCCEEDED(hr)) { pIX->Fx(); IY* pIY = NULL; hr = pIX->QueryInterface(IID_IY, (void**)&pIY); if (SUCCEEDED(hr)) { pIY->Fy(); pIY->Release(); }

第43页:客户方代码示例(续)

 IZ* pIZ = NULL; hr = pIX->QueryInterface(IID_IZ, (void**)&pIZ); if (SUCCEEDED(hr)) { pIZ->Fz(); pIZ->Release(); } pIX->Release(); } else { cout << "Client:\t\tCould not create component. hr = " << hex << hr << endl; } CoUninitialize(); return 0; }

第44页:服务器方代码:全局定义

服务器方代码:

#include <iostream.h> #include <objbase.h> #include "Iface.h"#include "Registry.h"static HMODULE g_hModule = NULL;static long g_cComponents = 0;static long g_cServerLocks = 0;// Friendly name of component const char g_szFriendlyName[] = "Inside COM, Ch 7 Example";const char g_szVerIndProgID[] = "InsideCOM.Chap07";

第45页:构件类 CA 的定义

class CA : public IX, public IY { public: virtual HRESULT __stdcall QueryInterface(const IID& iid, void** ppv); virtual ULONG __stdcall AddRef(); virtual ULONG __stdcall Release(); virtual void __stdcall Fx() { cout << "Fx" << endl; } virtual void __stdcall Fy() { cout << "Fy" << endl; } CA(); ~CA(); private: long m_cRef; };

第46页:CA::QueryInterface实现

HRESULT __stdcall CA::QueryInterface(const IID& iid, void** ppv) { if (iid == IID_IUnknown) { *ppv = static_cast<IX*>(this); } else if (iid == IID_IX) { *ppv = static_cast<IX*>(this); } else if (iid == IID_IY) { *ppv = static_cast<IY*>(this); } else { *ppv = NULL; return E_NOINTERFACE; } reinterpret_cast<IUnknown*>(*ppv)->AddRef(); return S_OK; }

第47页:类厂类 CFactory 的定义

class CFactory : public IClassFactory { public: virtual HRESULT __stdcall QueryInterface(const IID& iid, void** ppv); virtual ULONG __stdcall AddRef(); virtual ULONG __stdcall Release(); virtual HRESULT __stdcall CreateInstance(IUnknown* pUnknownOuter, const IID& iid, void** ppv); virtual HRESULT __stdcall LockServer(BOOL bLock); CFactory() : m_cRef(1) {} ~CFactory() {} private: long m_cRef; };

第48页:CFactory的IUnknown实现

HRESULT __stdcall CFactory::QueryInterface(const IID& iid, void** ppv) { if ((iid == IID_IUnknown) || (iid == IID_IClassFactory)) { *ppv = static_cast<IClassFactory*>(this); } else { *ppv = NULL; return E_NOINTERFACE; } reinterpret_cast<IUnknown*>(*ppv)->AddRef(); return S_OK; } ULONG __stdcall CFactory::AddRef() { return InterlockedIncrement(&m_cRef); } ULONG __stdcall CFactory::Release() { if (InterlockedDecrement(&m_cRef) == 0) { delete this; return 0; } return m_cRef; }

第49页:类厂创建实例的实现

// IClassFactory implementation HRESULT __stdcall CFactory::CreateInstance(IUnknown* pUnknownOuter, const IID& iid, void** ppv) { if (pUnknownOuter != NULL) { return CLASS_E_NOAGGREGATION; } CA* pA = new CA; if (pA == NULL) { return E_OUTOFMEMORY; } HRESULT hr = pA->QueryInterface(iid, ppv); pA->Release(); return hr; }

第50页:LockServer与卸载判定

// LockServer HRESULT __stdcall CFactory::LockServer(BOOL bLock) { if (bLock) { InterlockedIncrement(&g_cServerLocks); } else { InterlockedDecrement(&g_cServerLocks); } return S_OK; } STDAPI DllCanUnloadNow() { if ((g_cComponents == 0) && (g_cServerLocks == 0)) { return S_OK; } else { return S_FALSE; } }

第51页:DllGetClassObject 实现

STDAPI DllGetClassObject(const CLSID& clsid, const IID& iid, void** ppv) { if (clsid != CLSID_Component1) return CLASS_E_CLASSNOTAVAILABLE; CFactory* pFactory = new CFactory; if (pFactory == NULL) { return E_OUTOFMEMORY; } HRESULT hr = pFactory->QueryInterface(iid, ppv); pFactory->Release(); return hr; }

第52页:构件的注册与注销

STDAPI DllRegisterServer() { return RegisterServer(g_hModule, CLSID_Component1, g_szFriendlyName, g_szVerIndProgID, g_szProgID); } STDAPI DllUnregisterServer() { return UnregisterServer(CLSID_Component1, g_szVerIndProgID, g_szProgID); }

第53页:DllMain

BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, void* lpReserved) { if (dwReason == DLL_PROCESS_ATTACH) { g_hModule = hModule; } return TRUE; }

第54页:构件创建与调用时序图

创建与调用构件的时序:client → COM Lib 的 CoGetClassObject → DLL 的 DllGetClassObject → New CFactory → 返回 IClassFactory → IClassFactory::CreateInstance(IID_IX) → New CA → pIX->Fx() → IClassFactory::Release。

本页以时序图为主,文字版为图中的调用顺序,建议对照原图查看。

顶部目录