2009-10-2
======================
《深入解析MFC》笔记 11. MFC 实现 COM
======================
COM ( Component Object Model,组件对象模型)
OLE 是微软的对象技术的名字,是一种整合软件的统一方法,使得软件可以随时间而演变,是一种整合技术。
从时间的角度讲,OLE是最早出现的,然后是COM和 ActiveX;
从体系结构角度讲,OLE和ActiveX是建立在COM之上的,所以COM是基础;
OLE的主要目标是使得应用程序被外界所认识和理解。
使用COM编程的目的是使得软件从源代码以及的重用上升到二进制一级的重用。
组件是一个可重用的模块,由一组处理过程、数据封装和用户接口组成的业务对象(Rules Object)。组件看起来像对象。它们的主要区别是:
1)组件可以在另一个称为容器(有时也称为承载者或宿主)的应用程序中使用,也可以作为独立过程使用;
2)组件可以由一个类构成,也可以由多个类组成,或者是一个完整的应用程序;
3)组件为模块重用,而对象为代码重用。
组件模型有COM(Component Object Model,对象组件模型)/DCOM(Distributed COM,分布式对象组件模型)和CORBA(Common Object Request Broker Architecture,公共对象请求代理体系结构)。
=================
COM
------------------------
COM 类
COM 所做的全部工作就是在二进制一级共享软件。
C++ 类 和 COM 类
1、C++使用操作符new来实例化一个对象,而COM对象通过API的函数来创建。
2、C++使用delete来删除一个对象,COM使用引用计数来控制对象的生存期,引用计数为0,自动删除。
3、C++使用特殊的机制来控制运行时类型信息(run-time type info)和类型转化(typecasting),而COM类通过成员函数来支持
4、C++类提供成员函数和数据以供外部访问,COM中,类只提供成员函数。
5、COM类没有具体规定的布局。
6、COM引入了一套接口的规范来形式化对象指针的概念。
使用COM对象:
①、给操作系统发送实例化一个COM的请求;
②、得到对象的一个接口指针
③、使用接口; ④、释放接口
------------------------
COM 接口
一个接口就是一组等待被实现的纯虚函数
GUID
Globally Unique Identifier 128位数字
COM 类的ID用 "CLSID"作为前缀,
接口 ID 用"IID"作为前缀
=================
IUnknown接口
struct IUnknown{
virtual HRESULT QueryInterface ( IID& iid, void** ppvObj ) = 0;
virtual ULONG AddRef() = 0;
virtual ULONG Release() = 0;
}
-------
对象生存期管理
COM对象自己控制生存期。
COM 对象的引用计数由 IUnknown 接口的 AddRef() 和Release() 这两个函数来管理。
--------
接口协商(negotiation,运行时发现功能)
当一个客户拥有了一个COM对象的一个接口指针,就能够通过 QueryInterface() 发现这个对象所支持的别的接口
HRESULT QueryInterface ( REFIID riid, LPVOID far* ppvObj );
riid用来标识所要得到的接口的 GUID。
ppvObj 用来存放接口指针
实现 IUnknown::QueryInterface的一个方法:
HRESULT CoSomeObject::QueryInterface ( IID& riid, void FAR** ppvObj ){
HRESULT hr = ResultFromSCode ( E_NOINTERFACE );
*ppvObj = NULL;
if ( riid == IID_IUnknown ){
*ppvObj = ( IPersist * ) this;
hr = NOERROR;
} else if (riid == IID_IPersist ) {
*ppvObj = ( IPersist * ) this;
hr = NOERROR;
}
return hr;
}
-------------
调用 、 使用 、释放。
1、调用某个方法得到一个接口(通过初始化一个对象或者通过一个存在的接口调用 QueryInterface())。
2、使用接口内的函数。
3、调用Release() 对对象引用计数减1.
========================
COM 对象服务器
COM类存在于服务器之中。COM服务器分为两种: 进程内服务器(in-proc)和进程外服务器(out-proc)。
in-proc server 是 DLL形式的,他们与客户在同一个进程空间内。
out-proc server 是作为 EXE文件形式存在,和客户在不同的进程空间内(甚至在不同机器).
进程内服务器(DLL)
进程外服务器(EXE)
有更强的鲁棒性,进程外服务器需要付出额外的代价,列集(marshaling)。
一个进程空间内的地址(数据地址、函数指针)在另一个进程空间内不起作用,需要用远程过程调用(RPC)和列集(marshaling)。
marshaling:
每一个接口,在客户代码中有一个代理(proxy),一个代理是一个接口的进程内实现。
服务端中,每一个特定的接口都建立了一份存根(stub)代码(在服务器进程空间内)。存根是RPC传输的接收端,
它把代理传过来的函数调用和参数传递映射为映射为服务器进程内的函数调用和参数传递。
客户通过接口进行函数调用时,代理将参数打包为一个32位可移植的数据结构。打包完后,对服务器进程产生一个远程过程调用。
服务器端,存根保存真正的接口指针、函数和相关的数据结构。存根将参数从包中解开以在本进程空间内调用。
一个函数调用返回时,存根将返回值和其他返回信息打包,发送回代理。代理解包,取出返回值供客户使用。
类厂(class factory)
COM通过类厂来创建对象,进程空间内的每一个COM类都有一个类厂与之相对应。类厂产生的是对象,不是类。
类厂是一个特殊COM类。
一个重要的接口是 IClassFactory。它包含两个函数:CreateInstance()和LockServer()。
客户通过调用CreateInstance来创建OLE类的实例,LockServer来增加整个服务器的引用计数。
每一个COM类在源代码一级都有自己的类厂。
进程内服务器通过一个预先定义的函数将类厂提供给客户,
进程外服务器将类厂注册到 Windows的注册表中。
在进程内服务器提供类厂
通过导出函数(exported function)来提供类厂,函数名为:DLLGetClassObject()。
客户可以通过 CoGetClassObject() 或 CoCreateInstance() 来创建一个 COM 对象。它们都调用了DLLGetClassObject来得到相应COM类的类厂接口指针。
然后可以调用类厂的CreateInstance() 来创建一个类的实例并且得到一个接口指针。
STDAPI DllGetClassObject ( REFCLSID rclsid, REFIID riid, LPVOID FAR* ppv)
要得到的类厂的COM类的GUID值,第三个参数 是一个指向接口指针的指针,用来存放请求的接口指针的地方。
在进程外服务器中提供类厂:
在运行时在注册表里注册类厂,CoRegisterClassObject():
HRESULT CoRegisterClassObject ( REFCLSID clsid, // 要注册的类的GUID
IUnknown *pUnk, //对象的 IUnknown 指针
DWORD grfContext, //参数用来标识服务器的环境——可执行的服务器代码是一个DLL、一个本地服务器还是一个远程服务器
DWORD grfFlags, //用来表示客户使用何种方式跟服务器建立连接
LPDWORD pdwRegister ); //指向DWORD的指针,所指向的值在类厂注册的时候由COM来填写。该值标识了类厂的注册信息,可以通过
它然后调用 CoRevokeClassObject() 来取消类厂的注册。
卸载进程内服务器
调用COM中的 CoFreeUnusedLibraries() 来实现这个功能,该函数通过调用DLL的DLLCanUnloadNow()来询问是否能被卸载。
卸载进程外服务器
进程外服务器是自己卸载自己,
· 当客户减少服务器的引用计数(通过调用IClassFactory::LockServer ( FALSE ),而且外部没有对象了。
· 当客户释放最后一个对象的最后一个接口指针,并且服务器的锁计数是0.
posted on 2010-03-15 23:28
Euan 阅读(931)
评论(0) 编辑 收藏 引用 所属分类:
windows