《03Com接口与对象》由会员分享,可在线阅读,更多相关《03Com接口与对象(77页珍藏版)》请在金锄头文库上搜索。
1、COM接口与对象接口与对象潘爱民北京大学计算机研究所2003-9-19http:/ IDLvvCOM对象从历史看从历史看COMvvCOM产生的背景9393年因为年因为OLE 2OLE 2的需要而产生的需要而产生OLE 1OLE 1的缺陷的缺陷vvCOM又从OLE中脱颖而出COMCOM的优势不限于的优势不限于OLEOLECOMCOM成为成为MicrosoftMicrosoft跟上跟上InternetInternet的一的一项重要基础技术项重要基础技术vv今天的Windows平台上,COM无处不在COMCOM和和. .NETNETCOM基础基础三个概念三个概念vvCOM组件组件:可独立发布的二进制
2、组件组件:可独立发布的二进制组件在在WindowsWindows平台上为平台上为DLLDLL或者或者EXEEXEvvCOM对象通过通过COMCOM接口提供服务接口提供服务符合符合OOOO中对象的基本概念中对象的基本概念vvCOM接口客户与对象之间的协议,对象实现客户与对象之间的协议,对象实现COMCOM接口,客户使用接口,客户使用COMCOM接口接口如何设计?如何设计?vvCOM组件为方便起见,只讨论为方便起见,只讨论WindowsWindows平台上平台上DLLDLL类型的组件类型的组件vvCOM对象如何标识一个对象?对象以什么形式如何标识一个对象?对象以什么形式存在?客户如何创建对象?存在
3、?客户如何创建对象?对象如何暴露接口?一个或是多个?对象如何暴露接口?一个或是多个?vvCOM接口要求:跨编译器、跨语言、跨平台要求:跨编译器、跨语言、跨平台设计设计COM接口接口从从C+入手入手vvC+类:接口与实现的分离接口:类的接口:类的publicpublic部分部分class CMyStringprivate: char *m_psz;public: CMyString(const char * psz); CMyString(); const char*Find(const char *psz); int Length();设计设计COM接口接口从从C+入手入手(续续)vvC+类的
4、实现CMyString:CMyString(const char * psz) : m_psz( new charpsz ? strlen(psz)+1 :1) if ( psz ) strcpy(m_psz,psz); else m_psz0 = 0;CMyString:CMyString() delete m_psz;const char*CMyString:Find(const char *psz) return strstr(m_psz,psz);int CMyString:Length() return strlen(m_psz);C+类的链接类的链接(linking)vv静态链接许
5、多类库的做法许多类库的做法编译时刻的链接编译时刻的链接vv静态链接的缺点代码重复:多个程序各有自己的代码重复:多个程序各有自己的代码,需要更多的内存代码,需要更多的内存客户程序占据更多的外存空间客户程序占据更多的外存空间库代码更新需要重新编译所有的库代码更新需要重新编译所有的客户程序客户程序C+类的链接类的链接linking(续续)vv动态链接运行时刻的链接运行时刻的链接vv动态链接形式编译时刻通过引入库编译时刻通过引入库运行时刻完全动态运行时刻完全动态vvDll Hell#ifdef MYSTRINGDLL#define EXPORTORIMPORT _declspec(dllexport)
6、 #else#define EXPORTORIMPORT _declspec(dllimport)#endifclass EXPORTORIMPORT CMyStringprivate: char *m_psz;public: CMyString(const char * psz); CMyString(); const char*Find(const char *psz); int Length();C+接口如何走向接口如何走向COM接口接口vv动态链接符合COM的需要vvC+中类形式的接口存在的问题客户看到了什么?客户看到了什么?若用若用Visual C+ 5.0/6.0Visual C+
7、 5.0/6.0编译器编译器?0CMyStringQAEPBDZ?1CMyStringQAEXZ?FindCMyStringQAEPBDPBDZ?LengthCMyStringQAEHXZ客户眼中的客户眼中的C+类类(续续)vv如果是Borland C+编译器(4.02)CMyString$bctr$qpxc CMyString$bdtr$qv CMyStringFind$qpxc CMyStringLength$qv vv问题1:名字冲突命名冲突解决方案命名冲突解决方案1vv模块定义文件(.def)中给出别名LIBRARY MYSTRINGEXPORTS CMyString$bctr$qpx
8、c=?0CMyStringQAEPBDZ CMyString$bdtr$qv=?1CMyStringQAEXZ CMyStringFind$qpxc=?FindCMyStringQAEPBDPBDZ CMyStringLength$qv=?LengthCMyStringQAEHXZ命名冲突解决方案命名冲突解决方案2vv模块定义文件(.def)中给出序号别名LIBRARY MYSTRINGEXPORTS ?0CMyStringQAEPBDZ 1 ?1CMyStringQAEXZ 2 ?FindCMyStringQAEPBDPBDZ 3 ?LengthCMyStringQAEHXZ 4LIBRAR
9、Y MYSTRINGEXPORTS CMyString$bctr$qpxc 1 CMyString$bdtr$qv 2 CMyStringFind$qpxc 3 CMyStringLength$qv 4命名冲突解决方案命名冲突解决方案3vv使用C+类的vtablevvC+类的vtable不随编译器而变化vvvtable包含了各个函数的原型,顺序固定,每个函数的参数、返回类型等,函数名并不重要vvvtable要求这些接口函数必须是虚函数vv客户如何得到vtable?仍然需要有一种办法来创建仍然需要有一种办法来创建C+C+类类new/deletenew/delete?需要对象的二进制结构需要对象的
10、二进制结构问题问题2 2C+对象的二进制结构对象的二进制结构vvC+的封装是语法上的封装,而不是二进制封装vvnew/delete是编译器相关的编译器不仅要知道编译器不仅要知道publicpublic信息,也要知道信息,也要知道privateprivate信息信息vvC+对象的二进制结构是编译器相关的vv即使客户看到的C+类公开接口没有变化,但是C+类的实现改变了,仍然会打破客户与对象之间的连接C+对象与客户之间的连接问题对象与客户之间的连接问题vv客户与C+对象之间的连接点越小越好只有接口部分必要的信息才放入接口只有接口部分必要的信息才放入接口把把C+C+类的实现细节与接口分开类的实现细节与
11、接口分开提取出针对所有编译器都不变的因素作为提取出针对所有编译器都不变的因素作为客户与对象共享的接口信息客户与对象共享的接口信息vv方案1:句柄句柄方案句柄方案vv方案1:句柄#ifndef CMyStringclass CMyString;#endifclass EXPORTORIMPORT IMyStringprivate: CMyString *m_pthis;public: IMyString(const char * psz); IMyString(); const char*Find(const char *psz); int Length();class CMyStringpri
12、vate: char *m_psz; int m_nLength;public: CMyString(const char * psz); CMyString(); const char*Find(const char *psz); int Length();纯虚基类方案纯虚基类方案vv前提条件:在给定平台上所有的编译器都会产生同在给定平台上所有的编译器都会产生同样的二进制结构样的二进制结构纯虚函数在单继承情况下满足这一条件纯虚函数在单继承情况下满足这一条件vv纯虚基类只包含虚函数,限定每个虚函数的调用习惯vv对于跨平台的情形,我们肯定要通过中间层,所以暂时可以不考虑虚函数的继承布局情况虚函数
13、的继承布局情况class B : class B : pulicpulic A A private : private : int int value1;value1; public: public: virtual void Func1(void) virtual void Func1(void) virtual void Func2(void) virtual void Func2(void);变量 偏移量vptr 0value1 4B:Func1B:Func2vtable纯虚基类方案例子纯虚基类方案例子vv解决了名字冲突解决了名字冲突vv解决了解决了C+C+类的二进制布局不兼容问题类的二
14、进制布局不兼容问题 客户只看到客户只看到vtablevtable,没有看到其他的实现细节没有看到其他的实现细节 保证不同语言编写的程序可以互操作保证不同语言编写的程序可以互操作 在不改变接口的情况下,可以单独升级客户或者对象在不改变接口的情况下,可以单独升级客户或者对象class IString virtual const char*Find(const char *psz)=0; virtual int Length()=0;变量 偏移量 vptr 0FindLengthvtable纯虚接口的使用?纯虚接口的使用?vv假如有一个C+对象实现了IStringvv客户怎么使用?怎么拿到怎么拿到v
15、tablevtable接口接口#include istring.hclass CMyString : public IStringprivate: char *m_psz;public: CMyString(const char * psz); CMyString(); const char*Find(const char *psz); int Length();如何创建对象?如何创建对象?vv不能使用newvvDLL的唯一接口是引出函数vv可行方案:单独提供一个引出函数供客户调用extern C _declspec(dllexport) IString *CreateString(const
16、 char *psz);extern C IString *CreateString(const char *psz) return new CMyString(psz);通过引出函数创建对象通过引出函数创建对象extern C _declspec(dllimport)IString *CreateString(const char *psz);void main() IString *p; p = CreateString(Hello); if (p) const char*psz = p-Find(llo); int n = p-Length(); ;创创建建对对象象#include is
17、tring.htypedef IString * (*PfnCreateString)(const char *psz);void main() IString *p; HANDLE h = LoadLibrary(c:tempmystring.dll); if (NULL!=h) PfnCreateString pfn = (PfnCreateString)GetProcAddress(h,CreateString); if (pfn) p = pfn(Hello); if (p) const char*psz = p-Find(llo); int n = p-Length(); / Be
18、careful about calling FreeLibrary. ;如何删除对象?如何删除对象?vv删除对象发生在客户与对象建立联系之后,所以比较好办vv但是不能用deletevv可以让对象自己把自己删除vv在IString中增加一个方法class IString virtual void Delete()=0; virtual const char*Find(const char *psz)=0; virtual int Length()=0;删除对象自身删除对象自身#include istring.hclass CMyString : public IStringprivate: ch
19、ar *m_psz;public: CMyString(const char * psz); virtual CMyString(); void Delete(); const char*Find(const char *psz); int Length();void CMyString:Delete() delete this;#include istring.hvoid main() IString *p; p = CreateString(Hello); if (p) const char*psz = p-Find(llo); int n = p-Length(); p-Delete()
20、; ;小结小结vv我们已经建立起对象与客户之间的基本通信方式vv更高的要求:接口的升级接口的升级增加新的功能增加新的功能生命周期管理生命周期管理vv什么时候该删除对象什么时候该删除对象vv多个客户共享同一个对象,如何管理?多个客户共享同一个对象,如何管理?对象的进化对象的进化vv在原有接口的基础上增加新的功能,例如class IString virtual void Delete(); virtual const char*Find(const char *psz); virtual int Length(); virtual char FindAt(int index);新客户老的对象对象的
21、进化(续)对象的进化(续)vv完全增加新的功能class IPersist virtual void Delete(); virtual void Save(const char *pszFile); virtual void Load(const char *pszFile);客户对象IStringIPersist?接口的进化接口的进化vv对象的接口不能发生变化如果接口中需要增加新的方法,可以派生出如果接口中需要增加新的方法,可以派生出新的接口来新的接口来class IString2 : public IStringvirtual char FindAt(int index);vv新对象实现
22、两个接口:IString2和IString不打断新的客户与老的对象之间的关系不打断新的客户与老的对象之间的关系但是客户必须明确地知道对象是否实现了自但是客户必须明确地知道对象是否实现了自己感兴趣的接口己感兴趣的接口对象实现多个接口对象实现多个接口vv假如对象实现了两个接口IString和IPersist客户需要在客户需要在runtimeruntime时明确地知道接口的类型时明确地知道接口的类型信息,包括通过创建函数得到的初始接口类型信息,包括通过创建函数得到的初始接口类型at runtimeat runtime,客户可以灵活地从一个接口变换客户可以灵活地从一个接口变换到另一个接口,如果对象不支
23、持某个接口,客到另一个接口,如果对象不支持某个接口,客户也有办法知道户也有办法知道回忆回忆RTTI(Runtime type identification)RTTI(Runtime type identification)vvdynamic_castdynamic_castvvRTTIRTTI依赖于编译器的实现依赖于编译器的实现vvRTTIRTTI只能用于类的继承层次中只能用于类的继承层次中对象实现多个接口对象实现多个接口(续一续一)vv所以我们需要自己构造一套类型机制,要求:每个接口都要提供类型转换机制,能转换到同每个接口都要提供类型转换机制,能转换到同一对象上实现的其他接口一对象上实现的其
24、他接口客户只要得到了一个接口就可以得到其他的接客户只要得到了一个接口就可以得到其他的接口,所以创建函数可以返回任一个接口口,所以创建函数可以返回任一个接口如果对象不支持某个接口,客户必须能明确地如果对象不支持某个接口,客户必须能明确地知道,而不是发生异常知道,而不是发生异常 robustrobustDynamic_castDynamic_cast对象实现多个接口对象实现多个接口(续二续二)vv一个对象实现IString2和IString接口:class IString virtual void Delete()=0; virtual void *Dynamic_cast(const char
25、*psz)=0; virtual const char*Find(const char *psz)=0; virtual int Length()=0;class IString2 : public IString virtual char FindAt(int index)=0;对象实现多个接口对象实现多个接口(续三续三)vv实现Dynamic_castclass CMyString : public IString2.void *CMyString:Dynamic_cast(const char *psz) if (strcmp(psz,IString)=0) return static_
26、cast(this); else if (strcmp(psz,IString2)=0) return static_cast(this); return NULL;对象实现多个接口对象实现多个接口(续四续四)#include istring.hvoid main() IString *p = CreateString(Hello); if (p) IString2 *p2; const char*psz = p-Find(llo); int n = p-Length(); if (p2=(IString2 *)p-Dynamic_cast(IString2) char c = p2-Find
27、At(3); p-Delete(); ;class IPersist virtual void Delete()=0; virtual void *Dynamic_cast(const char *psz)=0; virtual void Save(const char *pszFile)=0; virtual void Load(const char *pszFile)=0;对象实现多个接口对象实现多个接口(续五续五)vv一个对象实现两个没有继承关系的接口#include istring.hclass CMyString : public IString2, public IPersist
28、.void *CMyString:Dynamic_cast(const char *psz) if (strcmp(psz,IString)=0) return static_cast(this); else if (strcmp(psz,IString2)=0) return static_cast(this); else if (strcmp(psz,IPersist)=0) return static_cast(this); return NULL;对象实现多个接口对象实现多个接口(续六续六)void main() IString *p = CreateString(Hello); if
29、 (p) IString2 *p2; IPersist *p3; const char*psz = p-Find(llo); int n = p-Length(); if (p2=(IString2 *)p-Dynamic_cast(IString2) char c = p2-FindAt(3); if (p3=(IPersist *)p-Dynamic_cast(IPersist) p3-Save(c:tempstr.txt); p-Delete(); ;对象实现多个接口对象实现多个接口(续七续七)接口的转换接口的转换vv每个接口提供一个用于接口转换的函数vv对象实现接口的时候,可以使用C+
30、编译器本身提供的类型转换功能vv每个接口的Dynamic_cast函数决定了客户可以访问其他哪些接口对象的生命周期管理对象的生命周期管理vv对象只需要被删除一次。每个接口都有对象只需要被删除一次。每个接口都有DeleteDelete函数?还是只有一个接口才有?进函数?还是只有一个接口才有?进一步,一步,什么时候删除对象?什么时候删除对象?vv客户可能拥有多个指向对象的引用,客户可能拥有多个指向对象的引用,每个每个引用各有自己的引用各有自己的lifetimelifetimevv每个引用从被有效赋值开始,一直到生命每个引用从被有效赋值开始,一直到生命周期结束,这期间被称为:周期结束,这期间被称为:
31、outstanding outstanding reference reference 未完结引用未完结引用vv客户管理每个引用的客户管理每个引用的lifetimelifetime,也就是说它也就是说它要要显式显式地告诉对象引用无效了地告诉对象引用无效了对象的生命周期管理对象的生命周期管理(续续)vv每个对象要管理一个被称为引用计数每个对象要管理一个被称为引用计数( (reference count)reference count)的整数值。的整数值。vv为了有效地管理对象的生命周期,它应该提为了有效地管理对象的生命周期,它应该提供一些规则和操作,供客户遵守和使用。供一些规则和操作,供客户遵守
32、和使用。vv规则:保持引用计数的确切含义,也就是记规则:保持引用计数的确切含义,也就是记录当前录当前outstanding referenceoutstanding reference的数目。引用的数目。引用计数从计数从0 0开始,首次把接口递交给客户时为开始,首次把接口递交给客户时为1 1,以后由客户管理,当引用计数回到,以后由客户管理,当引用计数回到0 0时,时,删除自己。删除自己。vv当客户通过复制获得新的接口指针时,引用当客户通过复制获得新的接口指针时,引用计数加一,当某个接口不用时,减一计数加一,当某个接口不用时,减一引用计数的两个操作引用计数的两个操作vv我们用引用计数我们用引用计
33、数的两个管理操作的两个管理操作代替原来简单的代替原来简单的DeleteDelete函数函数class IString virtual void DestroyPointer()=0; virtual void *Dynamic_cast(const char *psz)=0; virtual void DuplicatePointer()=0; virtual const char*Find(const char *psz)=0; virtual int Length()=0;class IPersist virtual void DestroyPointer()=0; virtual voi
34、d *Dynamic_cast(const char *psz)=0; virtual void DuplicatePointer()=0; virtual void Save(const char *pszFile)=0; virtual void Load(const char *pszFile)=0;实现引用计数的两个操作实现引用计数的两个操作CMystring:CMyString(const char * psz) : m_psz( new charpsz ? strlen(psz)+1 :1), m_refcount(0) if ( psz ) strcpy(m_psz,psz);
35、else m_psz0 = 0;void CMyString:DestroyPointer() if (0m_refcount) m_refcount-; if (0=m_refcount) delete this;void CMyString:DuplicatePointer() m_refcount+;#include istring.hclass CMyString : public IString2, public IPersistprivate: char *m_psz; long m_refcount;public: CMyString(const char * psz); CMy
36、String(); void DuplicatePointer(); void DestroyPointer(); void *Dynamic_cast(const char *); const char*Find(const char *psz); int Length(); char FindAt(int index); void Save(const char *pszFile); void Load(const char *pszFile);接口转换时刻相当于接口复制接口转换时刻相当于接口复制void *CMyString:Dynamic_cast(const char *psz) v
37、oid *p = NULL; if (strcmp(psz,IString)=0) p = static_cast(this); else if (strcmp(psz,IString2)=0) p = static_cast(this); else if (strcmp(psz,IPersist)=0) p = static_cast(this); if (NULL!=p) m_refcount+; return p;客户管理对象的生命周期客户管理对象的生命周期void main() IString *p = CreateString(Hello); if (p) IString2 *p2;
38、 IPersist *p3; const char*psz = p-Find(llo); int n = p-Length(); if (p2=(IString2 *)p-Dynamic_cast(IString2) char c = p2-FindAt(3); p2-DestroyPointer(); if (p3=(IPersist *)p-Dynamic_cast(IPersist) p3-Save(c:tempstr.txt); p3-DestroyPointer(); p-DestroyPointer(); ;extern C void *CreateString(const cha
39、r *psz, const char *pszinterface) void *pret = NULL; CMyString *p = new CMyString(psz); if (NULL!=p) pret= p-Dynamic_cast(pszinterface); if (NULL=pret) delete p; return pret;修改创建函数修改创建函数vv让创建函数也正确地维护引用计数接口整理接口整理vv每个接口都需要下面的三个函数Dynamic_castDynamic_castDuplicatePointerDuplicatePointerDeletePointerDele
40、tePointervv把三个函数放到一个基接口中,所有的接口都从这个基接口派生class IAnyInterfacevirtual void *DynamicCast(const char *psz)=0;virtual void DuplicatePointer()=0;virtual void DestroyPointer()=0;其他尚待考虑的问题其他尚待考虑的问题vvDll什么时候被卸载?vv如何标识一个接口?字符串?vv线程安全?vv如何标识一个对象?对象的身份?vv跨进程?跨机器?对象环境?vvCOM接口接口vv概念:函数集,以二进制的形式给出了从一方到另一方的调用规范vv接口标识
41、IIDvvIUnknownvvCOM接口二进制结构COM接口的标识接口的标识IIDvv是是GUIDGUID的一种用法的一种用法vvGUIDGUID是一个是一个128128位的长整数位的长整数vv产生规则保证了唯一性产生规则保证了唯一性vv例子:例子:5454BF6567-1007-11D1-B0AA-444553540000BF6567-1007-11D1-B0AA-444553540000vvC C语言结构和定义:语言结构和定义:typedef structtypedef struct _GUID _GUID DWORD DWORD Data1;Data1; WORD WORD Data2;
42、Data2; WORD WORD Data3;Data3; BYTE BYTE Data48;Data48; GUID; GUID;extern C const GUID CLSID_MYSPELLCHECKER = extern C const GUID CLSID_MYSPELLCHECKER = 0x54bf6567, 0x1007, 0x11d1, 0x54bf6567, 0x1007, 0x11d1, 0xb0, 0xaa, 0x44, 0x45, 0x53, 0xb0, 0xaa, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00 ;0x54, 0x00, 0x
43、00 ;IUnknown接口接口vv所有的COM接口都从IUnknown派生vvC+定义:class class IUnknown IUnknown public:public:virtual HRESULT_virtual HRESULT_stdcall QueryInterfacestdcall QueryInterface( (const IID& const IID& iidiid, void *, void *ppvppv) = 0 ;) = 0 ;virtual ULONGvirtual ULONG _ _stdcall AddRefstdcall AddRef() = 0; ()
44、 = 0; virtual ULONGvirtual ULONG _ _stdcall stdcall Release() = 0;Release() = 0;IUnknown接口接口(续续)vvC定义: typedef struct IUnknownVtbltypedef struct IUnknownVtbl HRESULT ( STDMETHODCALLTYPE _RPC_FAR *HRESULT ( STDMETHODCALLTYPE _RPC_FAR *QueryInterfaceQueryInterface )( )( IUnknownIUnknown _RPC_FAR * Thi
45、s, /* in */ REFIID _RPC_FAR * This, /* in */ REFIID riidriid, , /* /* iidiid_isout */ void _RPC_FAR *_RPC_FAR *_isout */ void _RPC_FAR *_RPC_FAR *ppvObjectppvObject); ); ULONG ( STDMETHODCALLTYPE _RPC_FAR * ULONG ( STDMETHODCALLTYPE _RPC_FAR *AddRefAddRef )( )( IUnknownIUnknown _RPC_FAR * This); _RP
46、C_FAR * This); ULONG ( STDMETHODCALLTYPE _RPC_FAR * ULONG ( STDMETHODCALLTYPE _RPC_FAR *ReleaseRelease )( )( IUnknownIUnknown _RPC_FAR * This); _RPC_FAR * This); IUnknownVtblIUnknownVtbl; ;interface interface IUnknownIUnknown CONST_VTBL CONST_VTBL struct IUnknownVtblstruct IUnknownVtbl _RPC_FAR * _R
47、PC_FAR *lpVtbllpVtbl; ; ; ;COM接口结构接口结构C语言描述示例语言描述示例IDictionarystruct IDictionaryVtblstruct IDictionaryVtbl; ;struct IDictionarystruct IDictionary IDictionaryVtblIDictionaryVtbl * * pVtblpVtbl; ;struct IDictionaryVtblstruct IDictionaryVtbl /* /* QueryInterfaceQueryInterface, , AddRefAddRef, Release *
48、/, Release */BOOL (*Initialize)( BOOL (*Initialize)( IDictionaryIDictionary * this); * this);BOOL (*BOOL (*LoadLibraryLoadLibrary)( )( IDictionaryIDictionary * this, String); * this, String);BOOL (*BOOL (*InsertWordInsertWord)( )( IDictionaryIDictionary * this, String, String); * this, String, Strin
49、g);void (*void (*DeleteWordDeleteWord)( )( IDictionaryIDictionary * this, String); * this, String);BOOL (*BOOL (*LookupWordLookupWord)( )( IDictionaryIDictionary * this, String, String *); * this, String, String *);BOOL (*BOOL (*RestoreLibraryRestoreLibrary )( )( IDictionaryIDictionary * this, Strin
50、g); * this, String);void (*void (*FreeLibraryFreeLibrary)( )( IDictionaryIDictionary * this); * this);C+语言描述示例语言描述示例IDictionaryclass class IDictionaryIDictionary : public : public IUnknownIUnknown virtual BOOL Initialize() = 0;virtual BOOL Initialize() = 0;virtual BOOL virtual BOOL LoadLibraryLoadLi
51、brary(String) = 0;(String) = 0;virtual BOOL virtual BOOL InsertWordInsertWord(String, String) = 0;(String, String) = 0;virtual void virtual void DeleteWordDeleteWord(String) = 0;(String) = 0;virtual BOOL virtual BOOL LookupWordLookupWord(String, String *) = 0;(String, String *) = 0;virtual BOOL virt
52、ual BOOL RestoreLibraryRestoreLibrary(String) = 0;(String) = 0;virtual void virtual void FreeLibraryFreeLibrary() = 0;() = 0;COM接口的内存模型接口的内存模型COM接口的内存模型接口的内存模型(续一续一)COM接口的内存模型接口的内存模型(续二续二)接口查询接口查询vv目的:按照COM规范,一个COM对象可以实现多个接口。从一个接口到另一个接口的访问途径vv函数QueryInterface(iid, ppv) vv用法:初始得到了一个接口指针之后,调用它的初始得到了一个
53、接口指针之后,调用它的QueryInterfaceQueryInterface函数,获得另一个接口指针函数,获得另一个接口指针vv返回值说明了对象对接口的支持情况vvS_OK、E_NOINTERFACE、E_UNEXPECTED接口查询用法示例接口查询用法示例/loadthedictionaryretValue=pIDictionary-LoadLibrary(eng_ch.dict);if(retValue=FALSE)pIDictionary-Release();return;.ISpellCheck*pISpellCheck;HRESULTresult=pIDictionary-Quer
54、yInterface(IID_SpellCheck,(void*)&pISpellCheck);if(result!=S_OK)pIDictionary-Release();return;/.useinterfacepISpellCheck/finally,releasedictionaryobjectpIDictionary-Release();pISpellCheck-Release();.QueryInterface实现实现vv与对象的定义有关vvIUnknown必须是个静态接口指针,其他接口指针可以是动态的vvQueryInterface在返回接口之前,必须调用新接口的AddRef函数
55、QueryInterface实现举例实现举例class class CDictionaryCDictionary : public : public IDictionaryIDictionary , public , public ISpellCheckISpellCheck public :public :CDictionaryCDictionary();(); CDictionaryCDictionary();();public :public :/ / IUnknownIUnknown member function member functionvirtual HRESULT virt
56、ual HRESULT QueryInterfaceQueryInterface(const IID& (const IID& iidiid, void *, void *ppvppv) ;) ;virtual ULONGvirtual ULONG AddRefAddRef() ; () ; virtual ULONGvirtual ULONG Release() ; Release() ;.private :private :int int m_Ref ;m_Ref ;.;QueryInterface实现举例(续一)实现举例(续一)QueryInterface实现举例(续二)实现举例(续二)
57、HRESULTHRESULTCDictionaryCDictionary:QueryInterfaceQueryInterface(constIID&(constIID&iidiid, ,void*void*ppvppv) ) if(if(iidiid=IID_=IID_IUnknownIUnknown)* *ppvppv=static_cast=static_castthis;*this;(IDictionaryIDictionary*)(*)(*ppvppv)-)-AddRefAddRef();();elseif(elseif(iidiid=IID_Dictionary)=IID_Dict
58、ionary)* *ppvppv=static_cast=static_castthis;*this;(IDictionaryIDictionary*)(*)(*ppvppv)-)-AddRefAddRef();();elseif(elseif(iidiid=IID_=IID_SpellCheckSpellCheck)* *ppvppv=static_cast=static_castthis;*this;(ISpellCheckISpellCheck*)(*)(*ppvppv)-)-AddRefAddRef();();elseelse* *ppvppv=NULL;=NULL;returnE_N
59、OINTERFACE;returnE_NOINTERFACE; returnS_OK;returnS_OK; COM对象的接口原则对象的接口原则vvIUnknown接口一致性vv接口对称性vv接口自反性vv接口传递性vv接口查询时间无关性引用计数引用计数vv目的:是为了控制对象的生命周期vv多个客户可以独立地控制对象的生存vv引用计数反映了被客户引用的个数Outstanding referencesOutstanding referencesvv引用计数是个整数,从0开始vv两个操作:增一和减一vv当引用计数为0时,表示没有客户在使用对象或者接口删除实现引用计数实现引用计数vv层次,或者粒度实
60、现引用计数三种方案实现引用计数三种方案vv组件级计数分辨率太粗计数分辨率太粗vv对象级vv接口级计数分辨率太细计数分辨率太细引用计数实现示例引用计数实现示例classCDictionary:publicIDictionarypublic:CDictionary();CDictionary();public:virtualHRESULTQueryInterface(constIID&iid,void*ppv);virtualULONGAddRef();virtualULONGRelease();virtualBOOLInitialize();virtualBOOLLoadLibrary(Stri
61、ng);/private:structDictWord*m_pData;char*m_DictFilename128;intm_Ref; 引用计数实现示例引用计数实现示例(续续)CDictionary:CDictionary()m_Ref=0;/.initializeULONGCDictionary:AddRef()m_Ref+;return(ULONG)m_Ref;ULONGCDictionary:Release()m_Ref-;return(ULONG)m_Ref;引用计数用法引用计数用法vv客户要引用接口时增一操作调用调用IUnknownIUnknown: :AddRefAddRef()
62、()vv客户用完接口时减一操作调用调用IUnknownIUnknown:Release():Release()vv当对象的引用计数为0时,释放vv当组件中所有对象的引用计数为0时,卸载vvAddRef和Release的返回值不可靠引用计数使用示例引用计数使用示例/loadthedictionaryBOOLretValue=pIDictionary-LoadLibrary(eng_ch.dict);if(retValue=FALSE)pIDictionary-Release();return;.IDictionary*pIDictionaryForWord=pIDictionary;pIDict
63、ionaryForWord-AddRef();/InsertordeletesomewordpIDictionaryForWord-InsertWord(.,.);pIDictionaryForWord-DeleteWord(.);pIDictionaryForWord-Release();./finally,releasedictionaryobjectpIDictionary-Release();使用引用计数规则使用引用计数规则vv函数参数输入参数输入参数输出参数输出参数输入输入- -输出参数输出参数vv局部接口指针变量vv全局接口指针变量vv类成员变量使用引用计数规则(续)使用引用计数规
64、则(续)vv一般性规则在顺序执行过程中,如果要对一个接口指针变在顺序执行过程中,如果要对一个接口指针变量赋值,则对赋值后的接口指针变量调用量赋值,则对赋值后的接口指针变量调用AddRefAddRef,并且,如果赋值前的接口指针变量并且,如果赋值前的接口指针变量还没有结束,则赋值前必须对它调用还没有结束,则赋值前必须对它调用ReleaseRelease以便先结束它的使用。以便先结束它的使用。如果要结束使用一个接口指针变量,以后不再如果要结束使用一个接口指针变量,以后不再用到它了,则调用用到它了,则调用ReleaseRelease函数。函数。vv优化引用计数问题引用计数问题vv在整个生存期内,Ad
65、dRef与Release一定要配对,否则:漏掉漏掉AddRefAddRef,程序出错程序出错漏掉漏掉ReleaseRelease,对象永不释放对象永不释放引用计数实现举例引用计数实现举例ULONG ULONG CDictionaryCDictionary: :AddRefAddRef () () m_Ref +;m_Ref +;return (ULONG) m_Ref;return (ULONG) m_Ref; ULONG ULONG CDictionaryCDictionary:Release ():Release () m_Ref -;m_Ref -;if (m_Ref = 0 )if (m_Ref = 0 ) delete this;delete this;return 0;return 0; return (ULONG) m_Ref;return (ULONG) m_Ref; 总结:总结:COM接口特点接口特点vv二进制特性vv接口不变性vv继承性(扩展性)vv多态性运行过程中的多态性