智能指針將引用計數隱藏起來,簡化客戶端。 CUnkown和CFactory通過提供可服用的IUnkown和ICfactory,簡化服務器端,即COM的編寫。
智能接口指針
智能指針的好處在于無需記住去調用Release
智能指針是重載了->操作符的類,智能指針接口類包含指向另一個對象的指針。此外,為使客戶相信智能指針和指針是相同的,智能指針還需要重載其他操作符,如*、&。
//// Ptr.h// - Smart interface pointer//#include <assert.h>//// iptr - Smart Interface Pointer// Use: IPtr<IX, &IID_IX> spIX ;// Do not use with IUnknown; IPtr<IUnknown, &IID_IUnknown>// will not compile. Instead, use IUnknownPtr.//template <class T, const IID* piid> class IPtr{public: // Constructors IPtr() { m_pI = NULL ; } IPtr(T* lp) { m_pI = lp ; if ( m_pI != NULL) { m_pI->AddRef() ; } } IPtr(IUnknown* pI) { m_pI = NULL ; if (pI != NULL) { pI->QueryInterface(*piid, (void **)&m_pI) ; } } // Destructor ~IPtr() { Release() ; } // Reset void Release() { if (m_pI != NULL) { T* pOld = m_pI ; m_pI = NULL ; pOld->Release() ; } } // Conversion Operator T*() { return m_pI ;} // Pointer operations T& operator*() { assert(m_pI != NULL) ; return *m_pI ;} T** operator&() { assert(m_pI == NULL) ; return &m_pI ;} T* operator->() { assert(m_pI != NULL) ; return m_pI ;} // Assignment from the same interface //調用了AddRef和Release,使得程序無需進行這些調用 //在給m_pI設置了新值值之后釋放當前指針,可防止在賦值之前相應的組件 //被從內存中刪除 T* operator=(T* pI) { if (m_pI != pI) { IUnknown* pOld = m_pI ; // Save current value. m_pI = pI ; // Assign new value. if (m_pI != NULL) { m_pI->AddRef() ; } if (pOld != NULL) { pOld->Release() ; // Release the old interface. } } return m_pI ; } // 將另外一個不同類型的接口指針賦值給一個智能指針,賦值操作自動調用 // QueryInterface,應對賦值后的智能指針進行檢查,以決定是否賦值成功 T* operator=(IUnknown* pI) { IUnknown* pOld = m_pI ; // Save current value. m_pI == NULL ; // Query for correct interface. if (pI != NULL) { HRESULT hr = pI->QueryInterface(*piid, (void**)&m_pI) ; assert(SUCCEEDED(hr) && (m_pI != NULL)) ; } if (pOld != NULL) { pOld->Release() ; // Release old pointer. } return m_pI ; } // Boolean functions BOOL operator!() { return (m_pI == NULL) ? TRUE : FALSE ;} // Requires a compiler that supports BOOL operator BOOL() const { return (m_pI != NULL) ? TRUE : FALSE ; } // GUID const IID& iid() { return *piid ;}PRivate: // Pointer variable T* m_pI ;} ;調用智能指針
void Think() { trace("Create Component 1.") ; IPtr<IX, &IID_IX> spIX ; HRESULT hr = CoCreateInstance(CLSID_Component1, NULL, CLSCTX_INPROC_SERVER, spIX.iid(), (void**)&spIX) ; if (SUCCEEDED(hr)) { trace("Succeeded creating component.") ; spIX->Fx() ; } }IPtr專用于IUknown的一個特殊版本—IUnknownPtr 之所以定義IUnknownPtr,是因為IPtr不能調用IUknown來實例化,否則會導致兩個具有相同原型的賦值運算符。
IPtr<IUknown &IID_IUKnown> spIUkown; //錯誤IUknownPtr spIUkown; //正確IUnknownPtr:
//// IUnknownPtr is a smart interface for IUnknown.//class IUnknownPtr{public: // Constructors IUnknownPtr() { m_pI = NULL ; } IUnknownPtr(IUnknown* lp) { m_pI = lp ; if ( m_pI != NULL) { m_pI->AddRef() ; } } // Destructor ~IUnknownPtr() { Release() ; } // Reset void Release() { if (m_pI) { IUnknown* pOld = m_pI ; m_pI = NULL ; pOld->Release() ; } } // Conversion operator IUnknown*() { return (IUnknown*)m_pI ;} // Pointer operations IUnknown& operator*() { assert(m_pI != NULL) ; return *m_pI ;} IUnknown** operator&() { assert(m_pI == NULL) ; return &m_pI ;} IUnknown* operator->() { assert(m_pI != NULL) ; return m_pI ;} // Assignment IUnknown* operator=(IUnknown* pI) { if (m_pI != pI) { IUnknown* pOld = m_pI ; // Save current value. m_pI = pI ; // Assign new value. if (m_pI != NULL) { m_pI->AddRef() ; } if (pOld != NULL) // Release the old interface. { pOld->Release() ; } } return m_pI ; } // Boolean functions BOOL operator!() { return (m_pI == NULL) ? TRUE : FALSE ;} operator BOOL() const { return (m_pI != NULL) ? TRUE : FALSE ; }private: // Pointer variable IUnknown* m_pI ;} ;帶智能指針的客戶端
代碼中所有的QueryInterface都被隱藏,提高了程序的可讀性
//// Client2.cpp - Client implementation with smart pointers//#include <objbase.h>#include "Iface.h"#include "Util.h" // Traces with labels for our output#include "Ptr.h" // Smart pointer classesstatic inline void trace(const char* msg) { Util::Trace("Client 2", msg, S_OK) ;} static inline void trace(const char* msg, HRESULT hr) { Util::Trace("Client 2", msg, hr) ;}void Think() { trace("Create Component 1.") ; IPtr<IX, &IID_IX> spIX ; HRESULT hr = CoCreateInstance(CLSID_Component1, NULL, CLSCTX_INPROC_SERVER, spIX.iid(), (void**)&spIX) ; if (SUCCEEDED(hr)) { trace("Succeeded creating component.") ; spIX->Fx() ; trace("Get interface IY.") ; IPtr<IY, &IID_IY> spIY ; spIY = spIX ; // Use Assignment. if (spIY) { spIY->Fy() ; trace("Get interface IX from IY.") ; IPtr<IX, &IID_IX> spIX2(spIY) ; // Use Constructor. if (!spIX2) { trace("Could not get interface IX from IY.") ; } else { spIX2->Fx() ; } } trace("Get interface IZ.") ; IPtr<IZ, &IID_IZ> spIZ ; spIZ = spIX ; if (spIZ) { spIZ->Fz() ; trace("Get interface IX from IZ.") ; IPtr<IX, &IID_IX> spIX2(spIZ) ; if (!spIX2) { trace("Could not get interface IX from IZ.") ; } else { spIX2->Fx() ; } } } else { trace("Could not create component.", hr) ; }}int main(){ // Initialize COM Library. CoInitialize(NULL) ; // Exercise the smart pointers. Think() ; // Uninitialize COM Library. CoUninitialize() ; return 0 ;}注意: 智能指針中的成員都是通過“.”來訪問的,而不是“->”
C++包裝類
一個包裝類是一個或多個COM接口的客戶,提供的是對這些接口使用的抽象。無論包裝類是否要改變接口的行為,都必須重新實現它所包裝的接口中的所有成員。
使用包裝類的原因: 1、如果希望封裝某個接口或某個接口對象,智能指針便不適合了,此時,可使用C++包裝類。 2、可以將幾個單獨的接口組合成一個邏輯單位。
未知接口基類
定義一個實現IUnkown的基類CUnkown,對于從CUnkown繼承的類,將不再考慮其AddRef或Release的實現問題,并且QueryInterface的實現也將得以簡化。
基類CUnkown將實現一個非代理IUnknown接口,而宏DECLARE_IUNKNOWN將實現一個代理IUnkown接口。
CUnkown實現非代理接口,代理接口由組件實現。
CUnkown實現INondelegatingUnkown接口的方法同被聚合組件實現此接口的方法相同。
CUnkown之所以要實現非代理接口,原因: 1、支持聚合 2、如果實現了代理接口,繼承它的組件重新改寫了IUnkown接口的純虛函數,CUnkown相當于無效了。
#ifndef __CUnknown_h__#define __CUnknown_h__#include <objbase.h>/////////////////////////////////////////////////////////////// Nondelegating IUnknown interface// - Nondelegating version of IUnknown//interface INondelegatingUnknown{ virtual HRESULT __stdcall NondelegatingQueryInterface(const IID& iid, void** ppv) = 0 ; virtual ULONG __stdcall NondelegatingAddRef() = 0 ; virtual ULONG __stdcall NondelegatingRelease() = 0 ;} ;/////////////////////////////////////////////////////////////// Declaration of CUnknown // - Base class for implementing IUnknown//class CUnknown : public INondelegatingUnknown{public: // Nondelegating IUnknown implementation virtual HRESULT __stdcall NondelegatingQueryInterface(const IID&, void**) ; virtual ULONG __stdcall NondelegatingAddRef() ; virtual ULONG __stdcall NondelegatingRelease() ; // Constructor //接收一個指向外部IUnkown接口的指針作為參數,并保存起來 //供GetOuterUnkown函數使用 CUnknown(IUnknown* pUnknownOuter) ; // Destructor virtual ~CUnknown() ; //Init和FinalRelease //都是虛函數,幫助派生類對內部組件進行管理 // Initialization (especially for aggregates) //幫助派生類創建被包容或聚合的組件 virtual HRESULT Init() { return S_OK ;} // Notification to derived classes that we are releasing //給被刪除組件提供一個釋放其所保持的指向內部組件接口指針的機會 //將增大引用計數,以免相應組件被釋放被包容組件上的接口時 //引起Release的遞歸調用 virtual void FinalRelease() ; // Count of currently active components static long ActiveComponents() { return s_cActiveComponents ;} // Helper function // 是為了簡化派生類中對NondelegatingQueryInterface的實現 HRESULT FinishQI(IUnknown* pI, void** ppv) ;protected: // Support for delegation IUnknown* GetOuterUnknown() const { return m_pUnknownOuter ;}private: // Reference count for this object long m_cRef ; // Pointer to (external) outer IUnknown IUnknown* m_pUnknownOuter ; // Count of all active instances static long s_cActiveComponents ; } ;/////////////////////////////////////////////////////////////// 代理 IUnknown// - Delegates to the nondelegating IUnknown, or to the// outer IUnknown if the component is aggregated.//避免每次實現一個組件都去實現代理IUnkown接口//#define DECLARE_IUNKNOWN / virtual HRESULT __stdcall / QueryInterface(const IID& iid, void** ppv) / { / return GetOuterUnknown()->QueryInterface(iid,ppv) ; / } ; / virtual ULONG __stdcall AddRef() / { / return GetOuterUnknown()->AddRef() ; / } ; / virtual ULONG __stdcall Release() / { / return GetOuterUnknown()->Release() ; / } ;///////////////////////////////////////////////////////////#endif/////////////////////////////////////////////////////////////// CUnknown.cpp // - Implementation of IUnknown base class//#include "CUnknown.h"#include "Util.h"static inline void trace(char* msg) {Util::Trace("CUnknown", msg, S_OK) ;} static inline void trace(char* msg, HRESULT hr) {Util::Trace("CUnknown", msg, hr) ;}/////////////////////////////////////////////////////////////// Count of active objects// - Use to determine if we can unload the DLL.//long CUnknown::s_cActiveComponents = 0 ;/////////////////////////////////////////////////////////////// Constructor//CUnknown::CUnknown(IUnknown* pUnknownOuter): m_cRef(1){ // Set m_pUnknownOuter pointer. if (pUnknownOuter == NULL) { trace("Not aggregating; delegate to nondelegating IUnknown.") ; m_pUnknownOuter = reinterpret_cast<IUnknown*> (static_cast<INondelegatingUnknown*> (this)) ; // notice cast } else { trace("Aggregating; delegate to outer IUnknown.") ; m_pUnknownOuter = pUnknownOuter ; } // Increment count of active components. ::InterlockedIncrement(&s_cActiveComponents) ;}//// Destructor//CUnknown::~CUnknown(){ InterlockedDecrement(&s_cActiveComponents) ;}//// FinalRelease - called by Release before it deletes the component//void CUnknown::FinalRelease(){ trace("Increment reference count for final release.") ; m_cRef = 1 ;}//// Nondelegating IUnknown// - Override to handle custom interfaces.//HRESULT __stdcall CUnknown::NondelegatingQueryInterface(const IID& iid, void** ppv){ // CUnknown supports only IUnknown. if (iid == IID_IUnknown) { return FinishQI(reinterpret_cast<IUnknown*> (static_cast<INondelegatingUnknown*>(this)), ppv) ; } else { *ppv = NULL ; return E_NOINTERFACE ; }}//// AddRef//ULONG __stdcall CUnknown::NondelegatingAddRef(){ return ::InterlockedIncrement(&m_cRef) ;}//// Release//ULONG __stdcall CUnknown::NondelegatingRelease(){ ::InterlockedDecrement(&m_cRef) ; if (m_cRef == 0) { FinalRelease() ; delete this ; return 0 ; } return m_cRef ;}//// FinishQI// - 是為了簡化派生類中對NondelegatingQueryInterface的實現// //HRESULT CUnknown::FinishQI(IUnknown* pI, void** ppv) { *ppv = pI ; pI->AddRef() ; return S_OK ;}類廠
在實現了一個組件后,需要為該組件實現一個類廠。
CFactory實現IClassFactory,可以同任何從CUnknown派生的COM組件一起工作。借助于CLSID和其他信息,CFactory將能夠創建相應的組件。
CFactory還提供了可用于實現DLL入口點如DllGetObject的代碼。
———–組件數據———–
typedef HRESULT (*FPCREATEINSTANCE)(IUnknown*, CUnknown**) ;/////////////////////////////////////////////////////////////// CFactoryData// - Information CFactory needs to create a component// supported by the DLL//class CFactoryData{public: // 組件類標識符 const CLSID* m_pCLSID ; // 指向組件創建函數的指針 FPCREATEINSTANCE CreateInstance ; // 保存在Windows注冊表中的一個易記名稱 const char* m_RegistryName ; // ProgID const char* m_szProgID ; // 與版本無關的 ProgID const char* m_szVerIndProgID ; // 查找CLSID的幫助函數 BOOL IsClassID(const CLSID& clsid) const { return (*m_pCLSID== clsid) ;}} ;———–每個組件的CFactoryData結構填充———
#include "CFactory.h"#include "Iface.h"#include "Cmpnt1.h"#include "Cmpnt2.h"#include "Cmpnt3.h"http://///////////////////////////////////////////////////////////// Server.cpp//// This file contains the component server code.// The FactoryDataArray contains the components that // can be served.//// Each component derived from CUnknown defines a static function// for creating the component with the following prototype. // HRESULT CreateInstance(IUnknown* pUnknownOuter, // CUnknown** ppNewComponent) ;// This function is used to create the component.////// The following array contains the data used by CFactory// to create components. Each element in the array contains// the CLSID, the pointer to the creation function, and the name// of the component to place in the Registry.//CFactoryData g_FactoryDataArray[] ={ {&CLSID_Component1, CA::CreateInstance, "Inside COM, Chapter 9 Example, Component 1", //易記名稱 "InsideCOM.Chap09.Cmpnt1.1", // ProgID "InsideCOM.Chap09.Cmpnt1"}, // 版本無關ProgID {&CLSID_Component2, CB::CreateInstance, "Inside COM, Chapter 9 Example, Component 2", "InsideCOM.Chap09.Cmpnt2.1", "InsideCOM.Chap09.Cmpnt2"}, {&CLSID_Component3, CC::CreateInstance, "Inside COM, Chapter 9 Example, Component 3", "InsideCOM.Chap09.Cmpnt3.1", "InsideCOM.Chap09.Cmpnt3"}} ;int g_cFactoryDataEntries = sizeof(g_FactoryDataArray) / sizeof(CFactoryData) ;————進程中服務器———–
//// GetClassObject// - 基于 CLSID 創建類廠//HRESULT CFactory::GetClassObject(const CLSID& clsid, const IID& iid, void** ppv){ if ((iid != IID_IUnknown) && (iid != IID_IClassFactory)) { return E_NOINTERFACE ; } // 遍歷數組,查找與客戶待創建的組件的相應的CFactoryData結構 // g_FactoryDataArray在server.cpp中實現 for (int i = 0; i < g_cFactoryDataEntries; i++) { const CFactoryData* pData = &g_FactoryDataArray[i] ; if (pData->IsClassID(clsid)) { // 將CFactoryData結構傳給類廠 // 這樣就能知道需要創建哪種組件 *ppv = (IUnknown*) new CFactory(pData) ; if (*ppv == NULL) { return E_OUTOFMEMORY ; } return NOERROR ; } } return CLASS_E_CLASSNOTAVAILABLE ;}//// IClassFactory implementation// 創建組件//HRESULT __stdcall CFactory::CreateInstance(IUnknown* pUnknownOuter, const IID& iid, void** ppv){ // 只有當所請求的IID是IID_IUnknown,才進行聚合 if ((pUnknownOuter != NULL) && (iid != IID_IUnknown)) { return CLASS_E_NOAGGREGATION ; } // 創建組件,使用保存在CFactoryData數組創建函數實例指針 // 調用被創建組件的構造函數 // 將相應的指針通過pNewComponent返回給調用者 // 若第一個參數非空,則組件將是被聚合的 CUnknown* pNewComponent ; HRESULT hr = m_pFactoryData->CreateInstance(pUnknownOuter, &pNewComponent) ; if (FAILED(hr)) { return hr ; } // 初始化組件 hr = pNewComponent->Init() ; if (FAILED(hr)) { // 初始化失敗,釋放組件 pNewComponent->NondelegatingRelease() ; return hr ; } // 得到要求的接口 hr = pNewComponent->NondelegatingQueryInterface(iid, ppv) ; // 釋放類廠所持有的引用 pNewComponent->NondelegatingRelease() ; return hr ;}未知接口基類和類廠的使用
組件將不再重復實現AddRef和Release了,只需將新接口加入到QueryInterface實現中。并且只需要實現一個創建函數,無需實現一個完整的類廠。
———使用CUnkown實現IUnkown接口的組件———
組件2實現了IY,并聚合了組件3,而后者實現了接口IZ。 組件2被組件1聚合。 所以,組件2既是聚合組件,也是被聚合組件。
//// Cmpnt2.h - Component 2//#include "Iface.h"#include "CUnknown.h" // IUnknown基類/////////////////////////////////////////////////////////////// Component B// 繼承了CUnkown,CUnkown提供了非代理IUnkown的實現//class CB : public CUnknown, public IY{public: // 創建 static HRESULT CreateInstance(IUnknown* pUnknownOuter, CUnknown** ppNewComponent) ;private: // 聲明代理IUnknown. DECLARE_IUNKNOWN // 非代理 IUnknown // CUnkown不能完全實現QueryInterface,因為不知道組件所支持的接口 // 因此需要NondelegatingQueryInterface處理所支持的接口 virtual HRESULT __stdcall NondelegatingQueryInterface(const IID& iid, void** ppv) ; // 接口 IY virtual void __stdcall Fy() ; // 初始化 // 重載Init,以建立其他用于包容或聚合的組件 virtual HRESULT Init() ; // 清除 // 在CFactory::NondelegatingRelease刪除對象之前,將調用此函數 // 對于需要釋放指向內部組件指針的組件可以重載此函數 // CUnkown::FinalRelease將增大引用計數,防止對組件的析構遞歸 virtual void FinalRelease() ; // 構造函數 CB(IUnknown* pUnknownOuter) ; // 析構 ~CB() ; // 被聚合組件 IUnknown* m_pUnknownInner ; // 內部組件所支持的接口 IZ* m_pIZ ;} ;//// Cmpnt2.cpp - Component 2//#include <objbase.h>#include "Iface.h"#include "Util.h"#include "CUnknown.h" // IUnknown基類#include "Cmpnt2.h"static inline void trace(char* msg) {Util::Trace("Component 2", msg, S_OK) ;} static inline void trace(char* msg, HRESULT hr) {Util::Trace("Component 2", msg, hr) ;}/////////////////////////////////////////////////////////////// Interface IY implementation//void __stdcall CB::Fy(){ trace("Fy") ;}//// Constructor//CB::CB(IUnknown* pUnknownOuter): CUnknown(pUnknownOuter), m_pUnknownInner(NULL), m_pIZ(NULL){ // Empty}//// Destructor//CB::~CB(){ trace("Destroy self.") ;}//// NondelegatingQueryInterface implementation//HRESULT __stdcall CB::NondelegatingQueryInterface(const IID& iid, void** ppv){ if (iid == IID_IY) { //FinishQI是使在派生類中NondelegatingQueryInterface容易一些 return FinishQI(static_cast<IY*>(this), ppv) ; } else if (iid == IID_IZ) { return m_pUnknownInner->QueryInterface(iid, ppv) ; } else { //IUnkown和其它不知道的接口交由基類處理 return CUnknown::NondelegatingQueryInterface(iid, ppv) ; }}//// Initialize the component and create the contained component.//HRESULT CB::Init(){ trace("Create Component 3, which is aggregated.") ; HRESULT hr = CoCreateInstance(CLSID_Component3, GetOuterUnknown(), CLSCTX_INPROC_SERVER, IID_IUnknown, (void**)&m_pUnknownInner) ; if (FAILED(hr)) { trace("Could not create inner component.", hr) ; return E_FAIL ; } trace("Get pointer to interface IZ to cache.") ; hr = m_pUnknownInner->QueryInterface(IID_IZ, (void**)&m_pIZ) ; if (FAILED(hr)) { trace("Inner component does not support IZ.", hr) ; m_pUnknownInner->Release() ; m_pUnknownInner = NULL ; return E_FAIL ; } // Decrement the reference count caused by the QI call. trace("Got IZ interface pointer. Release reference.") ; GetOuterUnknown()->Release() ; return S_OK ;}//// FinalRelease - Called by Release before it deletes the component//void CB::FinalRelease(){ // 使用基類的函數,增加引用計數m_cRef,防止遞歸析構 CUnknown::FinalRelease() ; // Counter the GetOuterUnknown()->Release in the Init method. GetOuterUnknown()->AddRef() ; // Properly release the pointer, as there might be // per-interface reference counts. m_pIZ->Release(); // Release the contained component. // (We can do this now since we've released the interfaces.) if (m_pUnknownInner != NULL) { m_pUnknownInner->Release() ; }}/////////////////////////////////////////////////////////////// Creation function used by CFactory//HRESULT CB::CreateInstance(IUnknown* pUnknownOuter, CUnknown** ppNewComponent) { *ppNewComponent = new CB(pUnknownOuter) ; return S_OK ;}———-集成步驟————
使用CUnkown和CFactory編寫組件是比較簡單的。
1、編寫實現組件的類 1> 可以從CUnkown或其他CUnkown派生的類派生出待實現的組件 2> 使用DELCARE_IUNKOWN宏實現代理IUnkown接口 3> 在組件的構造函數中初始化CUnkown 4> 實現NondelegatingQueryInterface,在其中加入此組件支持而基類不支持的接口。對那些組件不支持的接口,可調用相應的基類。 5> 若需要在構建了組件之后進行其他一些初始化處理,可重載Init函數,以建立被包容或被聚合的組件。 6> 若需要在組件被刪除之前進行一些其他清理工作,可重載FinalRelease,以釋放被包容或被聚合的組件。 7> 給組件實現一個靜態的CreateInstance 8> 實現組件支持的那些借口。 2、對將要放到同一DLL中的組件重復上述步驟 3、編寫類廠 1> 建立一個文件,以包含全局CFactoryData數組g_FactoryDataArray 2> 定義g_FactoryDataArray數組并用DLL中所提供的信息填充此組件。 3> 定義g_FactoryDataEntires,其中包含g_FactoryDataArray中組件的個數。 4、編寫一個定義DLL入口點的DEF文件。 5、將上述所編寫的代碼同CUnkown.cpp和CFactory.cpp一塊編譯。
新聞熱點
疑難解答