COM接口与对象 潘爱民 panaimin@icst.pku.edu.cn 内容 !组件的接口 !COM接口 !COM IDL !COM对象 从历史看COM !COM产生的背景 – 93年因为OLE 2的需要而产生 – OLE 1的缺陷 !COM又从OLE中脱颖而出 – COM的优势不限于OLE – COM成为Microsoft跟上Internet的 一项重要基础技术 !今天的Windows平台上,COM无 处不在 COM基础——几个概念 !COM组件 –可独立发布的二进制组件 –在Windows平台上为DLL或者EXE !COM对象 –通过COM接口提供服务 –符合OO中对象的基本概念 !COM接口 –客户与对象之间的协议,对象实 现COM接口,客户使用COM接口 如何设计? !COM组件 –为 , Windows平台 上DLL 的组件 !COM对象 –如何 一个对象?对象 在?客户如何 对象? –对象如何 接口?一个或 个? !COM接口 –要 平台 设计COM接口——从C++ ? !C++ 接口与实现的¢£ –接口 的public?¢ class CMyString { private: char *m_psz; public: CMyString(const char * psz); ~CMyString(); const char*Find(const char *psz); int Length(); }; 设计COM接口——从C++ ?(¥) !C++ 的实现 CMyString::CMyString(const char * psz) : m_psz( new char[psz ? strlen(psz)+1 :1]) { if ( psz ) strcpy(m_psz,psz); else m_psz[0] = 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 !§currency1?接 –' “的?? – ?fi的?接 !§currency1?接的缺fl – –重? 个?· ???的 –,需要? 的内 –客户?·”?? 的… ‰间 –“ –? 需要重 ??的 客户?· C++ 的?接linking(¥) ! currency1?接 –`′?fi的?接 ! currency1?接 – ?fi通过? “ –`′?fi?ˉ currency1 #ifdef MYSTRINGDLL #define EXPORTORIMPORT _declspec(dllexport) #else #define EXPORTORIMPORT _declspec(dllimport) #endif class EXPORTORIMPORT CMyString { private: char *m_psz; public: CMyString(const char * psz); ~CMyString(); const char*Find(const char *psz); int Length(); }; C++接口如何?˙COM接口 ! currency1?接符合COM的需要 !C++中 的接口 在的¨ –客户看?? ? – 用Visual C++ 5.0/6.0 ??0CMyString@@QAE@PBD@Z ??1CMyString@@QAE@XZ ?Find@CMyString@@QAEPBDPBD@Z ?Length@CMyString@@QAEHXZ 客户?中的C++ (¥) !如? Borland C++ (4.02) @CMyString@$bctr$qpxc @CMyString@$bdtr$qv @CMyString@Find$qpxc @CMyString@Length$qv !¨ 1 ˇ— ˇ 1 ! 件(.def)中 出 ˇ LIBRARY MYSTRING EXPORTS @CMyString@$bctr$qpxc=??0CMyString@@QAE@PBD@Z @CMyString@$bdtr$qv=??1CMyString@@QAE@XZ @CMyString@Find$qpxc=?Find@CMyString@@QAEPBDPBD@Z @CMyString@Length$qv=?Length@CMyString@@QAEHXZ ˇ 2 ! 件(.def)中 出· ˇ LIBRARY MYSTRING EXPORTS ??0CMyString@@QAE@PBD@Z @1 ??1CMyString@@QAE@XZ @2 ?Find@CMyString@@QAEPBDPBD@Z @3 ?Length@CMyString@@QAEHXZ @4 LIBRARY MYSTRING EXPORTS @CMyString@$bctr$qpxc @1 @CMyString@$bdtr$qv @2 @CMyString@Find$qpxc @3 @CMyString@Length$qv @4 ˇ 3 !使用C++ 的vtable !C++ 的vtable不 而 ? !vtable a? 个 的 , ·? ,?个 的? o , ˇ 不重要 !vtable要 接口 ? !客户如何 ?vtable? –? 需要?一 ??? C++ – new/delete?需要对象的二进制??—— ¨ 2 C++对象的二进制?? !C++的 ?上的 ,而不 二 进制 !new/delete 的 – 不仅要知道public信息,也要知道 private信息 !C++对象的二进制?? 的 !即使客户看?的C++ 公开接口没? ?,但 C++ 的实现改 ?,? 会 打破客户与对象之间的连接 C++对象与客户之间的连接¨ !客户与C++对象之间的连接fl越小越好 – ?接口?¢?要的信息才放 接口 –把C++ 的实现细节与接口¢开 –提取出针对?? 都不 的因素作为 客户与对象共享的接口信息 ! 1 句柄 句柄 ! 1 句柄 #ifndef CMyString class CMyString; #endif class EXPORTORIMPORT IMyString { private: CMyString *m_pthis; public: IMyString(const char * psz); ~IMyString(); const char*Find(const char *psz); int Length(); }; class CMyString { private: char *m_psz; int m_nLength; public: CMyString(const char * psz); ~CMyString(); const char*Find(const char *psz); int Length(); }; 纯 基 !前提条件 –在 平台上??的 都会产生同 样的二进制?? –纯 在单继承情况下满足 一条件 !纯 基 a ,限 ?个 的调用习惯 !对于 平台的情 ,我们肯 要通过 中间层,? 暂?可 不考虑 的继承布局情况 class B : pulic A { private : int value1; public: virtual void Func1(void) virtual void Func2(void) }; 量 偏移量 value 0 vptr 4 value1 8 B::Func1 B::Func2 vtable 纯 基 例子 ! ?ˇ— ! ?C++ 的二进制布局不兼容¨ – 客户 看?vtable,没?看?其他的实现细节 – 保证不同 写的?·可 互操作 – 在不改 接口的情况下,可 单独升级客户或者对象 class IString { virtual const char*Find(const char *psz)=0; virtual int Length()=0; }; 量 偏移量 vptr 4 Find Length vtable 纯 接口的使用? !假如?一个C++对象实现?IString !客户怎 使用? –怎 拿?vtable接口 #include "istring.h" class CMyString : public IString { private: char *m_psz; public: CMyString(const char * psz); ~CMyString(); const char*Find(const char *psz); int Length(); }; 如何 对象? !不能使用new !DLL的唯一接口 ?出 !可′ 单独提供一个?出 供 客户调用 extern "C" _declspec(dllexport)IString *CreateString(const 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 "istring.h" typedef IString * (*PfnCreateString)(const char *psz); void main() { IString *p; HANDLE h = LoadLibrary("c:\\temp\mystring.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 careful about calling FreeLibrary. } }; 如何删除对象? !删除对象发生在客户与对象 立联系之 后,? 比较好? !但 不能用delete !可 让对象??把??删除 !在IString中增加一个 ? class IString { virtual void Delete()=0; virtual const char*Find(const char *psz)=0; virtual int Length()=0; }; 删除对象?身 #include "istring.h" class CMyString : public IString { private: char *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.h" void main() { IString *p; p = CreateString("Hello"); if (p) { const char*psz = p->Find("llo"); int n = p->Length(); p->Delete(); } }; 小? !我们已经 立 对象与客户之间的基本 通信 !?高的要 –接口的升级 –增加 的功能 –生 周期管 ! ? 删除对象 ! 个客户共享同一个对象,如何管 ? 对象的进? !在 ?接口的基础上增加 的功能,例如 class IString { virtual void Delete(); virtual const char*Find(const char *psz); virtual int Length(); virtual char FindAt(int index); }; 客户 的对象 对象的进? ¥ !?ˉ增加 的功能 class IPersist { virtual void Delete(); virtual void Save(const char *pszFile); virtual void Load(const char *pszFile); }; 客户 对象 IString IPersist ? 接口的进? !对象的接口不能发生 ? –如?接口中需要增加 的 ?,可 生出 的接口? class IString2 : public IString { virtual char FindAt(int index); }; ! 对象实现 个接口 IString2 IString –不打 的客户与 的对象之间的 系 –但 客户? 知道对象 实现?? ? 的接口 对象实现 个接口 !假如对象实现? 个接口IString IPersist –客户需要在runtime中 知道接口的 信息, 通过 ?的 接口 – at runtime,客户可 从一个接口 ? 一个接口,如?对象不 个接口,客 户???知道 – RTTI(Runtime type identification) !dynamic_cast !RTTI 于 的实现 !RTTI 能用于 的继承层 中 对象实现 个接口(¥一) !? 我们需要??? 一 ?制,要 –?个接口都要提供 ¢ ?制,能¢ ?同 一对象上实现的其他接口中 –客户 要 ??一个接口£可 ?其他的接 口,? 可 o ?一个接口 –如?对象不 个接口,客户? 能 知道,而不 发生¥? —— robust – Dynamic_cast 对象实现 个接口(¥二) !一个对象实现IString2 IString接口 class IString { virtual void Delete()=0; virtual void *Dynamic_cast(const char *psz)=0; virtual const char*Find(const char *psz)=0; virtual int Length()=0; }; class IString2 : public IString { virtual char FindAt(int index)=0; }; 对象实现 个接口(¥§) !实现Dynamic_cast class CMyString : public IString2{...} void *CMyString::Dynamic_cast(const char *psz) { if (strcmp(psz,"IString")==0) return static_cast<IString *>(this); else if (strcmp(psz,"IString2")==0) return static_cast<IString2 *>(this); return NULL; } 对象实现 个接口(¥currency1 currency1 ) #include "istring.h" void 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->FindAt(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; }; 对象实现 个接口(¥') !一个对象实现 个没?继承 系的接口 #include "istring.h" class CMyString : public IString2, public IPersist {...} void *CMyString::Dynamic_cast(const char *psz) { if (strcmp(psz,"IString")==0) return static_cast<IString *>(this); else if (strcmp(psz,"IString2")==0) return static_cast<IString2 *>(this); else if (strcmp(psz,"IPersist")==0) return static_cast<IPersist *>(this); return NULL; } 对象实现 个接口(¥“) void main() { IString *p = CreateString("Hello"); if (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:\\temp\\str.txt"); p->Delete(); } }; 对象实现 个接口(¥?) 接口的¢ !?个接口提供一个用于接口¢ 的 !对象实现接口的? ,可 使用C++ 本身提供的 ¢ 功能 !?个接口的Dynamic_cast ? 客户可 ?¨其他? 接口 对象的生 周期管 !对象 需要fi删除一 fl?个对象都 ?Delete ? ?一个对象都 ??进一–, ? 删除对象? !客户可能?? 个?˙对象的?用, ?个?用 ???的lifetime !?个?用从fi?· ?开 ,一?? 生 周期??, 期间fi?为 outstanding reference ”???用 !客户管 ?个?用的lifetime,也£ ?…要‰ ?对象?用无·? 对象的生 周期管 (¥) !?个对象要管 一个fi?为?用计 (reference count)的 ?fl !为??· 管 对象的生 周期,…` 提供一 ′? 操作,供客户?ˉ 使用 !′? 保 ?用计 的 ?a ,也£ ˙¨ 前outstanding reference的 ?fl ?用计 从0开 ,? 把接口 ? 客 户?为1, 后?客户管 , ?用计 ?0?,删除??fl ! 客户通过?制ˇ 的接口?针?,? 用计 加一, 个接口不用?,—一 ?用计 的 个操作 !我们用?用计 的 个管 操作 ? 单的 Delete 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 void *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 char[psz ? strlen(psz)+1 :1]), m_refcount(0) { if ( psz ) strcpy(m_psz,psz); else m_psz[0] = 0; } void CMyString::DestroyPointer() { if (0<m_refcount) m_refcount--; if (0==m_refcount) delete this; } void CMyString::DuplicatePointer() { m_refcount++; } #include "istring.h" class CMyString : public IString2, public IPersist { private: char *m_psz; long m_refcount; public: CMyString(const char * psz); ~CMyString(); 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); }; 接口¢ ?fi 于接口?制 void *CMyString::Dynamic_cast(const char *psz) { void *p = NULL; if (strcmp(psz,"IString")==0) p = static_cast<IString *>(this); else if (strcmp(psz,"IString2")==0) p = static_cast<IString2 *>(this); else if (strcmp(psz,"IPersist")==0) p = static_cast<IPersist *>(this); if (NULL!=p) m_refcount++; return p; } 客户管 对象的生 周期 void main() { IString *p = CreateString("Hello"); if (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); p2->DestroyPointer(); } if ((p3=(IPersist *)p->Dynamic_cast("IPersist"))) { p3->Save("c:\\temp\\str.txt"); p3->DestroyPointer(); } p->DestroyPointer(); } }; extern "C" void *CreateString(const char *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;} 改 !让 也 ?用计 接口 !?个接口都需要下 的§个 – Dynamic_cast – DuplicatePointer – DeletePointer !把§个 放?一个基接口中,??的 接口都从 个基接口 生 class IAnyInterface { virtual void *DynamicCast(const char *psz)=0; virtual void DuplicatePointer()=0; virtual void DestroyPointer()=0; }; 其他 考虑的¨ !Dll ? fi ? !如何 一个接口?—符 ? ! ? ˉ? !如何 一个对象?对象的身 ? ! 进?? ? ?对象 ?? !…… COM接口 !概念 , 二进制的 出? 从一 ? 一 的调用′a !接口 ——IID !IUnknown !COM接口二进制?? COM接口的 ——IID ! GUID的一 用? ! GUID 一个128 的 !产生′?保证?唯一 !例子 {54BF6567-1007-11D1-B0AA-444553540000} ! C ?? typedef struct _GUID { DWORD Data1; WORD Data2; WORD Data3; BYTE Data4[8]; } GUID; extern "C" const GUID CLSID_MYSPELLCHECKER = { 0x54bf6567, 0x1007, 0x11d1, { 0xb0, 0xaa, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00} } ; IUnknown接口 !??的COM接口都从IUnknown 生 !C++ class IUnknown { public: virtual HRESULT__stdcall QueryInterface( const IID& iid, void **ppv) = 0 ; virtual ULONG __stdcall AddRef() = 0; virtual ULONG __stdcall Release() = 0; }; IUnknown接口(¥) !C typedef struct IUnknownVtbl { HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( IUnknown __RPC_FAR * This, /* [in] */ REFIID riid, /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( IUnknown __RPC_FAR * This); ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( IUnknown __RPC_FAR * This); } IUnknownVtbl; interface IUnknown { CONST_VTBL struct IUnknownVtbl __RPC_FAR *lpVtbl; }; COM接口?? 接口?针 ?针 ?针 1 ?针 2 ?针 3 flflflflflfl 对象实现 vtablepVtable C ??例——IDictionary struct IDictionaryVtbl; struct IDictionary { IDictionaryVtbl * pVtbl; }; struct IDictionaryVtbl { /* … QueryInterface, AddRef, Release */ BOOL (*Initialize)( IDictionary * this); BOOL (*LoadLibrary)( IDictionary * this, String); BOOL (*InsertWord)( IDictionary * this, String, String); void (*DeleteWord)( IDictionary * this, String); BOOL (*LookupWord)( IDictionary * this, String, String *); BOOL (*RestoreLibrary )( IDictionary * this, String); void (*FreeLibrary)( IDictionary * this); }; C++ ??例—IDictionary class IDictionary : public IUnknown { virtual BOOL Initialize() = 0; virtual BOOL LoadLibrary(String) = 0; virtual BOOL InsertWord(String, String) = 0; virtual void DeleteWord(String) = 0; virtual BOOL LookupWord(String, String *) = 0; virtual BOOL RestoreLibrary(String) = 0; virtual void FreeLibrary() = 0; }; pVtable HRESULT QueryInterface(…) ULONG AddRef(); ULONG Release(); BOOL Initialize(this *) BOOL LoadLibrary(this *, String); BOOL InsertWord)( this *, String); void DeleteWord)( this *, String); BOOL LookupWord(this *, String, String *); BOOL RestoreLibrary(this *, String); void FreeLibrary(this *); IDictionary vtable this COM接口的内 客户使用的 接口?针 pIDictionary pVtable QueryInterface AddRef Release Initialize CDictionary 中 的?o实现 vtable m_pData m_DictFilename flflflflflfl COM接口的内 (¥一) 客户使用的 接口?针 pIDictionary1 pIDictionary2 pVtable QueryInterface AddRef Release Initialize CDictionary 中 的?o实现 vtable m_pData m_DictFilename pVtable m_pData m_DictFilename flflflflflfl COM接口的内 (¥二) — 对象1的 接口?针 pIDictionary1 — 对象2的 接口?针 pIDictionary2 pVtable QueryInterface AddRef ...... CDictionary 中 的?o实 现 vtable m_pData m_DictFilename pVtable — ? ...... QueryInterface AddRef ...... 一个— 对象 中 的? o实现 vtable 接口 !?的 COM′a,一个COM对象可 实现 个接口fl从一个接口? 一个接口 的?¨? ! QueryInterface(iid, ppv) !用? – ??一个接口?针之后,调用…的 QueryInterface ,ˇ 一个接口?针 !o ?? ?对象对接口的 情况 !S_OK E_NOINTERFACE E_UNEXPECTED 接口 用??例 // load the dictionary retValue = pIDictionary->LoadLibrary("eng_ch.dict"); if (retValue == FALSE) { pIDictionary->Release(); return; } ...... ISpellCheck *pISpellCheck; HRESULT result = pIDictionary->QueryInterface(IID_SpellCheck, (void **)&pISpellCheck); if (result != S_OK) { pIDictionary->Release(); return; } // ...... use interface pISpellCheck // finally, release dictionary object pIDictionary->Release(); pISpellCheck ->Release ( ); ...... QueryInterface实现 !与对象的 ? !IUnknown? 个§currency1接口?针,其他 接口?针可 currency1的 !QueryInterface在o 接口之前,? 调 用 接口的AddRef QueryInterface实现 例 class CDictionary : public IDictionary , public ISpellCheck { public : CDictionary(); ~CDictionary(); public : // IUnknown member function virtual HRESULT QueryInterface(const IID& iid, void **ppv) ; virtual ULONG AddRef() ; virtual ULONG Release() ; ...... private : int m_Ref ; ...... }; IUnknown IDictionary CDictionary IUnknown ISpellCheck QueryInterface实现 例 ¥一 IDictionary的vtable?针 this?针 ISpellCheck的vtable?针 CDictionary 的 ? QueryInterface AddRef Release ...... QueryInterface AddRef Release ...... IDictionary 成? ISpellCheck 成? vtable QueryInterface实现 例 ¥二 HRESULT CDictionary::QueryInterface(const IID& iid, void **ppv) { if ( iid == IID_IUnknown ){ *ppv = static_cast<IDictionary *>this ; ((IDictionary *)(*ppv))->AddRef() ; } else if ( iid == IID_Dictionary ) { *ppv = static_cast<IDictionary *>this ; ((IDictionary *)(*ppv))->AddRef() ; } else if ( iid == IID_SpellCheck ){ *ppv = static_cast<ISpellCheck * >this ; ((ISpellCheck *)(*ppv))->AddRef() ; } else { *ppv = NULL; return E_NOINTERFACE ; } return S_OK; } COM对象的接口 ? !IUnknown接口一 !接口对? !接口? !接口? !接口 ?间无 ?用计 !?的 为??制对象的生 周期 ! 个客户可 独立 ?制对象的生 !?用计 ??fi客户?用的个 – Outstanding references !?用计 个 ,从0开 ! 个操作 增一 —一 ! ?用计 为0?,??没?客户在使用 对象或者接口——删除 实现?用计 !层 ,或者 组件 对象1 IUnknown ISomeIntface 组件?用计 对象?用计 接口?用计 对象2 IUnknown IOtherIntface flflfl 对象?用计 接口?用计 接口?用计 接口?用计 实现?用计 § !组件级 –计 ¢ 太粗 !对象级 !接口级 –计 ¢ 太细 ?用计 实现?例 class CDictionary : public IDictionary { public : CDictionary(); ~CDictionary(); public : virtual HRESULT QueryInterface(const IID& iid, void **ppv) ; virtual ULONG AddRef() ; virtual ULONG Release() ; virtual BOOL Initialize(); virtual BOOL LoadLibrary(String); // …… private : struct DictWord *m_pData; char *m_DictFilename[128]; int m_Ref ; }; ?用计 实现?例(¥) CDictionary::CDictionary () { m_Ref = 0; // ... initialize } ULONG CDictionary::AddRef () { m_Ref ++; return (ULONG) m_Ref; } ULONG CDictionary::Release () { m_Ref --; return (ULONG) m_Ref; } ?用计 用? !客户要?用接口?——增一操作 –调用IUnknown::AddRef() !客户用?接口?———一操作 –调用IUnknown::Release() ! 对象的?用计 为0?,释放 ! 组件中??对象的?用计 为0?, !AddRef Release的o ?不可靠 ?用计 使用?例 // load the dictionary BOOL retValue = pIDictionary->LoadLibrary("eng_ch.dict"); if (retValue == FALSE) { pIDictionary->Release(); return; } ...... IDictionary *pIDictionaryForWord = pIDictionary; pIDictionaryForWord ->AddRef(); // Insert or delete some word pIDictionaryForWord ->InsertWord("...","..."); pIDictionaryForWord ->DeleteWord("..."); pIDictionaryForWord ->Release ( ); ...... // finally, release dictionary object pIDictionary->Release ( ); 使用?用计 ′? ! ? –输 ? –输出? –输 -输出? !局?接口?针 量 !ˉ局接口?针 量 ! 成? 量 使用?用计 ′? ¥ !一般 ′? –在 ·执′过?中,如?要对一个接口?针 量 ?,?对 ?后的接口?针 量调用 AddRef, 且,如? ?前的接口?针 量 没???,? ?前? 对…调用Release 先??…的使用fl –如?要??使用一个接口?针 量, 后不再 用?…?,?调用Release fl !优? ?用计 ¨ !在 个生 期内,AddRef与Release一 要配对, ? –漏掉AddRef,?·出错 –漏掉Release,对象永不释放 ?用计 实现 例 ULONG CDictionary::AddRef () { m_Ref ++; return (ULONG) m_Ref; } ULONG CDictionary::Release () { m_Ref --; if (m_Ref == 0 ) { delete this; return 0; } return (ULONG) m_Ref; } 总? COM接口特fl !二进制特 !接口不 !继承 (扩展 ) ! currency1 ——`′过?中的 currency1 接口 ? IDL interface IDictionary : IUnknown { HRESULT Initialize(); HRESULT LoadLibrary([in] string); HRESULT InsertWord([in] string, [in] string); HRESULT DeleteWord([in] string); HRESULT LookupWord([in] string, [out] string *); HRESULT RestoreLibrary([in] string); HRESULT FreeLibrary(); }; ! MIDL可 ?IDL 件生成C/C++接口 ? IDL 介 ! OSF IDL为基础 !基本 ? –与C 非?接近, ?? 联合 枚 typedef !interface !coclass !library –可 产生 “ IUnknown接口的IDL ? ! IDL接口 [ local, object, uuid(00000000-0000-0000-C000-000000000046), pointer_default(unique) ] interface IUnknown { typedef [unique] IUnknown *LPUNKNOWN; HRESULT QueryInterface( [in] REFIID riid, [out, iid_is(riid)] void **ppvObject); ULONG AddRef(); ULONG Release(); } IDL中 的 ? [ uuid(1e196b20-1f3c-1069-996b-00dd010fe676), version(1.0), helpstring("A class"), helpcontext(2481), appobject ] coclass myapp { [source] interface IMydocfuncs : IUnknown; dispinterface DMydocfuncs; }; IDL中“的 ? [ uuid(12345678-1234-1234-1234-123456789ABC), helpstring("Hello 2.0 Type Library"), lcid(0x0409), version(2.0) ] library Hello { /* Library definition statements */ }; IDL中library?例 [ object, uuid(. . .), <other interface attributes> ] interface IKnown : IUnknown { import "unknwn.idl"; <declarations, etc. for IKnown interface go here> }; [ <library attributes> ] library KnownLibrary { //reference interface IKnown: interface IKnown; //or create a new class: [ <coclass attributes> ] coclass KnowMore { interface IKnown; }; }; IDL的意 !IDL 无 – 的中间 !MIDL.exe产生C++头 件 – 于C++ !MIDL.exe产生TLB “ – COM本身提供?一 基础设施? 释 “ !??的 准接口都可 在SDK中找?IDL ? COM对象 !客户的?互实o ! ?,或者状currency1 操作 currency1 !能够提供服务——通过COM接口 !对象的实现?组件?ˉ ? COM对象的 ——CLSID ! GUID的一 用? ! 对象的? ? 要提供CLSID !COM对象的身 –身 一 的可判 COM对象与C++对象的比较 !层 差¥ ! 特 !可重用 ! currency1 的?现 不同 COM对象 接口图? — 对象 IUnknown IDictionary ISpellCheck 今天讲课内容的?考资料 !“COM 及`用”第二章 !“COM技术内幕”前currency1章 !“COM本质 ”第一 二章 !“COM Tutorial” 二选一 二选一 ??作业 !读?· –“COM 与`用”中 !第二章的“— 组件 — 客户”