页数: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)
内容:
(1)COM的起源:源于 OLE(Object Link and Embedding)。OLE1 采用 DDE(Dynamic Data Exchange)在不同的程序之间进行通信;DDE 缺点:效率低、稳定性不好、使用不方便。COM 是为克服上述不足而出现的,OLE2 以 COM 为基础,但 OLE 未能体现 COM 优点。
(2)什么是COM:构件对象模型(Component Object Model)是客户与构件为了能够互操作而遵循的标准。COM标准包括规范与实现两部分:
(3)COM构件:
构件与类:一个构件可以由多个类实现;接口与类:一个类可以实现多个接口。
(4)COM库(COM Library),功能:
DCOM的实现提供了分布式环境下的通信机制,在操作系统层次以DLL文件的形式存在。
(5)COM特性:
(6)COM发展趋势:
2 COM接口:COM接口是COM规范的核心内容。一个接口包含了一组函数;在C++中,可以用抽象基类来定义COM接口。接口的实现:vtbl 指针、虚拟函数表。
接口的内存结构:pIX 指向虚拟函数表,表中依次存放 Fx1、Fx2、Fx3、Fx4 的函数指针。
本页以内存结构示意图为主,建议对照原图查看。
Vtbl的作用:提高接口实现的灵活性。例子:一个实现接口IX的类CA,CA包含2个数据(Data1、Data2);pA 指向实例,实例中的 vtbl 指针指向虚拟函数表(Fx1~Fx4)。
本页以内存布局示意图为主,建议对照原图查看。
两个实例共享同一Vtbl:pA1 与 pA2 分别指向各自的 Data1、Data2,但通过各自的 vtbl 指针指向同一份虚拟函数表(Fx1~Fx4)。
本页以内存布局示意图为主,建议对照原图查看。
接口特点:
(1)功能提供:生存期控制、接口查询。IUnknown的定义(IDL):
interface IUnkown { virtual HRESULT QueryInterface(const IID&, void **ppv) = 0; virtual ULONG AddRef() = 0; virtual ULONG Release() = 0; }所有接口都必须继承 IUnknown:接口IX的虚拟函数表最前面是 QueryInterface、AddRef、Release,其后才是 Fx 等接口自身的函数。
本页以虚拟函数表结构示意图为主,建议对照原图查看。
IUnknown指针的获取:创建构件时,客户可以使用 CreateInstance,而不是 new。
IUnknown * CreateInstance() { IUnknown * pI = static_cast<IX *> (new foo); pI-> AddRef(); return pI; }(2)引用计数:AddRef 与 Release 控制构件的生命期,解决内存管理问题,使构件能够自己将自己删除。
使用规则:
引用计数的实现方式:可以在不同层次上进行计数——构件(构件引用记数)、对象(对象引用记数)、接口(接口引用记数)。
本页以三种计数层次的对比示意图为主:构件级为整个构件共享一个计数,接口级则为每个接口维护独立计数,建议对照原图查看。
(3)接口查询:一个COM对象(构件)可以实现多个接口,使用 QueryInterface 查询某个构件是否支持某个特定的接口。
void foo(Iunknown * pI) { IX * pIX = NULL; HRESULT hr = pI->QueryInterface(IID_IX, (void **)&pIX); if (SUCCEEDED(hr)) { pIX->Fx(); } }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; }QueryInterface的实现规则:
新版本构件的处理:COM接口永远不会变化——一个IID就是一个接口,通过发行新版本构件解决兼容性。当下列条件中的任何一个变化时应给新接口指定新IID:
不同版本接口的命名:在老名称后加一个数字。
3 COM实现(1)预备知识:DLL
HRESULT:向用户报告构件运行结果状况(“Here is the RESULT”),在 WINERROR.H 中进行定义,用户可以定义自己的代码。
GUID:Globally Unique IDentifier,用于标识构件(CLSID)与接口(IID),128位(长整数);由于空间足够大,接口标识冲突较小,可以用编程的方法生成具有唯一性的GUID(VC:UUIDGEN.EXE、GUIDGEN.EXE)。它借鉴了OSF的UUID(DCE中的RPC使用),对GUID值的传递通常通过引用进行。
CLSID:CLaSs IDentifier。
Windows注册表:Windows操作系统的共享系统数据库,包括硬件、软件配置及用户等各种信息,由 REGEDIT.EXE(95/98)、REGEDIT32.EXE(NT)启动,可以使用 REGSVR32.EXE 来注册某个构件。
注册表是一个由许多元素构成的层次结构,每一个元素均被称作是一个关键字,每一个关键字可以包含一系列子关键字等。例如:HKEY_CLASSES_ROOT、CLSID、ProgID 等。
本页为Windows注册表结构示意图,源文件仅提取到页面标签“COM基础”,建议对照原图查看。
本页为COM构件与注册表关系示意图,源文件仅提取到页面标签“COM基础”,建议对照原图查看。
(2)COM库:COM构件与客户皆需要完成的相同操作由 OLE32.DLL、OLE32.LIB 实现。COM库中常用的函数:
在使用COM库中的其他函数(除 CoBuildVersion)之前,进程必须先调用:
HRESULT CoInitialize(void * reserved)在退出之前调用:void CoUninitialize(void * reserved)。其它:CoBuildVersion、CoFreeUnusedLibraries;OleInitialize 建立在 CoInitialize 基础之上,DCOM 使用 CoInitializeEx 初始化。
与GUID(CLSID)相关的函数:例如将GUID转换为一个字符串:
wchar_t szCLSID[39]; int r = ::StringFromGUID2(CLSID_Component1, szCLSID, 39);其它:StringFromCLSID、StringFromIID、CLSIDFromString、IIDFromString、IsEqualGUID、IsEqualIID、IsEqualCLSID、CLSIDFromProgID。
COM库常用函数:
| 类别 | 函数 |
|---|---|
| 对象创建函数 | CoGetClassObject、CoCreateInstance、CoCreateInstanceEx、CoRegisterClassObject、CoRevokeClassObject、CoDisconnectObject |
| 内存管理函数 | CoTaskMemAlloc、CoTaskMemRealloc、CoTaskMemFree、CoGetMalloc |
(3)类厂(Class Factory):能够创建其他构件的构件(构件厂),其本身也是一个COM对象,支持一个特殊的接口 IClassFactory。每一个COM对象类应该有一个相应的类厂对象。
CoCreateInstance(COM库函数之一):
HRESULT __stdcall CoCreateInstance( const CLSID& clsid, IUnknown * pIUnknownOuter, DWORD dwClsContext, const IID& iid, void ** ppv);一个使用例子:
IX * pIX = NULL; HRESULT hr = ::CoCreateInstance(CLSID_Component1, NULL, CLSCTX_INPROC_SERVER, IID_IX, (void **) &pIX); if (SUCCEEDED(hr)) { pIX->FX(); pIX->Release(); }类厂:CoCreateInstance 实际上未直接创建COM构件,而是创建了一个被称为类厂的构件;类厂构件的唯一功能是创建其他构件,客户可以通过类厂所支持的接口来对类厂创建构件的过程加以控制。
CoCreateInstance 调用 CoGetClassObject 创建构件,创建构件的标准接口是 IClassFactory。
CoGetClassObject(创建类厂):
HRESULT __stdcall CoGetClassObject( const CLSID& clsid, DWORD dwClsContext, COSERVERINFO * pServerInfo, const IID& iid, void ** ppv);IClassFactory:用于创建构件的标准接口
Interface IClassFactory : IUnknown { HRESULT __stdcall CreateInstance(IUnknown * pUnknownOuter, const IID & iid, void ** ppv); HRESULT __stdcall LockServer(BOOL block); }未接收 CLSID:只能创建同某个CLSID相应的构件(扩展见 IClassFactory2)。
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; }在下列情况下应直接使用 CoGetClassObject 而不使用 CoCreateInstance:
(4)类厂的实现:CoGetClassObject 需要DLL中的一个特定的函数来创建构件的类厂:
STDAPI DllGetClassObject(const CLSID& clsid, const IID& iid, void ** ppv);创建、调用构件的过程:客户调用 COM库 的 CoGetClassObject → COM库调用 DLL 的 DllGetClassObject 创建类厂 → 返回 IClassFactory 指针 → 客户调用 IClassFactory::CreateInstance 创建构件 → 返回 IX 接口指针 → 调用 IX::Fx。
(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(); } 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; }服务器方代码:
#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";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; };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; }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; };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; }// 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; }// 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; } }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; }STDAPI DllRegisterServer() { return RegisterServer(g_hModule, CLSID_Component1, g_szFriendlyName, g_szVerIndProgID, g_szProgID); } STDAPI DllUnregisterServer() { return UnregisterServer(CLSID_Component1, g_szVerIndProgID, g_szProgID); }BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, void* lpReserved) { if (dwReason == DLL_PROCESS_ATTACH) { g_hModule = hModule; } return TRUE; }创建与调用构件的时序:client → COM Lib 的 CoGetClassObject → DLL 的 DllGetClassObject → New CFactory → 返回 IClassFactory → IClassFactory::CreateInstance(IID_IX) → New CA → pIX->Fx() → IClassFactory::Release。
本页以时序图为主,文字版为图中的调用顺序,建议对照原图查看。
下载地址:{{downurl}}
{{{a}}}
{{#b}}{{{b}}}
{{/b}} {{/title}} {{#nolog}}登陆注册
{{/nolog}} {{#djsx}}点我刷新
{{/djsx}} {{#gocz}} {{/gocz}} {{#ffqr}} {{/ffqr}}