編寫可複用性更好的C++代碼——Band對象和COMToys(八)

來源:互聯網
上載者:User

編譯/趙湘寧

原著:Paul Dilascia

MSJ November 1999 & December 1999

關鍵字:Bands 對象,Desk Bands,Info/Comm Bands,Explorer Bar,Tool Bands。

本文假設你熟悉C++,COM,IE。

下載本文原始碼: MyBands.zip (128KB)
                TestEditSrch.zip (75KB)

第一部分:Band 對象介紹
第二部分:BandObj的類層次和MyBands服務程式的註冊
第三部分:深入Band內部,揭開Band的面紗
第四部分:Band對象使用中遇到的一些問題
第五部分:建立自己的COM編程平台ComToys
第六部分:設計和構造COMToys
第七部分:類的實現

第八部分 類廠和註冊以及美中不足

類廠和註冊

    到現在為止,我們的討論沒有涉及到一個重要的細節,那就是對象的建立。因為相對與設計和實現來說,它比較簡單。前四個部分所討論的類工廠和註冊相當廣義。其中描述了BandObj如何利用資源指令檔中所列的DLL的條目以及使用ATL註冊器的類廠來註冊COM對象。到了COMToys,我僅僅是將代碼從BandObj移到了COMToys。 COMToys使用CTModule,CTFactory,和一個特殊的檔案DllEntry.cpp來處理對象的建立,註冊和DLL條目。CTModule是一個典型的“模組類”,就像MFC的CWinApp或者ATL的CComModule。其中的OnGetClassObject,OnDllRegisterServer等虛函數是可以重載的。不過必須包含(#include)實現DLL入口的DllEntry.cpp。

 extern "C" STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid,                           LPVOID* ppv) {   MFCENTRY;   return CTModule::GetModule()->     OnGetClassObject(rclsid, riid, ppv); }      

    如法炮製DllRegisterServer,DllCanUnloadNow及其它函數。CTModule::GetModule類似AfxGetApp。它返回一個且是僅有的一個全程CTModule對象。所以DllEntry.cpp的全部工作是將外部實現的硬性函數轉換成較容易重載的虛函數。 至於MFC應用方面,COMToys 有一個用於COM的MFC 應用類 CTMfcModule,它從CTModule 和 COleControlModule 實現多繼承。在這個CTMfcModule類中實現了OnGetClassObject和其它操作,這些操作中又調用相應的MFC函數來完成工作。將這些東西分離的理由是盡量將處理放到不依賴MFC的代碼中。圖十八顯示了MFC和非MFC類之間的關係: 


圖十八 MFC和非MFC類之間的關係

    關於類工廠的實現,COMToys 依賴於MFC模仿類:CTFactory。CTModule::OnRegisterServer調用一個靜態函數,CTFactory::OnRegisterAll,它具備針對每一個類工廠調用的CTFactory::OnRegister。當建立CTFactory的時候。必須指定的一個參數之一是資源ID。預設情況下,CTFactory::OnRegister尋找有相同ID的指令檔資源並調用ATL註冊器運行它。但首先它要調用另一個虛函數:OnInitRegistryVariables,以便初始化通用的註冊器變數,如%CLSID% 和%ClassName%.,下面是ComToys在 CTComObjFactory::OnInitRegistryVariables 定義的變數清單:

%CLSID%          = class ID (GUID) (COleObjectFactory::m_clsid)%MODULE%         = full pathname of DLL%Title%          = title (resource substring 0)%ClassName%      = human-readable COM class name (resource substring 1)%ProgID%         = ProgID (resource substring 2)%ThreadingModel% = "", "Apartment", "Free", or "Both" (m_nThreadingModel)      

可以重載 OnInitRegistryVariables 添加自己的註冊器變數。這些都與前幾部分的做法一樣,只是現在的代碼被從Band對象中抽象出來進入了COMToys。最終,在進行註冊與登出的處理時,所要做的全部工作是編寫一個註冊指令碼並將它作為資源放入資源檔,讓它的ID與COM類相同。

IDR_MYCOMCLASS REGISTRY DISCARDABLE "MyComClass.rgs"      

    應該指出,CTFactory有一個地方很傷腦筋:它本應該實現IClassFactory,但是它沒有。那是因為我還沒來得及顧上它。因為到目前為止我所建立的所有COM類都與MFC有關,我一直都用COleObjectFactory,它很好用。因此,我從COleObjectFactory 和 CTFactory 派生 CBandObjFactory。如果需要非MFC類工廠,可以為 CTFactory 實現一個IClassFactory。不過現在有CTFactory,COleObjectFactory相伴左右,CTFactory提供了所有註冊工作所需要的東西,很容易使用。而COleObjectFactory提供了IClassFactory介面和與DllGetClassObject的聯結。 當Windows資源管理員(Explorer)之類的進程想要建立自己的COM對象執行個體時——假設是建立MyBands.dll中的Band對象——它調用CoCreateInstance函數,這個函數讀取註冊表確定載入哪一個DLL並載入它,然後調用DllGetClassObject(在DllEntry.cpp中)函數,它再接著調用CTModule::OnGetClassObject。因為BandObj是一個基於MFC的對象,它使用CTMfcModule,其OnGetClassObject函數調用MFC的AfxDllGetClassObject函數。MFC搜尋類工廠清單及其它處理。到這裡也許有人會問:類工廠是在哪兒建立的呢?通常在MFC中建立類工廠用的是DECLARE_OLECREATE 和 IMPLEMENT_OLECREATE。這些宏聲明並執行個體化一個靜態COleObjectFactory對象:它的形式為CMyComClass::類工廠。但前面四個部分中的BandObj和COMToys都沒有使用DECLARE/IMPLEMENT_OLECREATE,因此,為了建立自己的類工廠,這就要調用new進行動態建立:

 // 在InitInstance中 new CTBandObjFactory(MYGUID,    RUNTIME_CLASS(CMyComClass),   IDR_MAINFRAME);      

    當建立了新的CTFactory,COMToys將它添加到主清單列表中;MFC對COleObjectFactory也做同樣的事情。換句話說,正像我在面幾個部分中解釋的那樣,不必專門做些什麼來讓MFC或COMToys知道你的類工廠。只要建立就行了。也不必刪除類工廠,CTModule會在ExitInstance中自動完成。如果寧願用老式方法將類工廠建立成一個靜態全域類,那也沒問題——但必須在自己的類工廠建構函式中設定m_bAutoDel = FALSE。避開DECLARE/IMPLEMENT_OLECREATE的主要理由是這樣做了以後,我就能按理想的方式派生自己的類工廠。在BandObj例子中,自己當然必須建立一個類工廠;如面幾個部分所做的那樣,依然要調用AddBandClass。 總之,為了處理對象的建立,所有要做的工作是從 CTFactory 和 COleObjectFactory 派生自己的類工廠並記住#include檔案DllEntry.cpp。剩下的事情由COMToys來做。為了讓COM對象自註冊,自己得寫一個用於註冊的資源指令碼(RGS檔案),並將它作為與類工廠有相同ID的REGISTRY資源添加到.RC檔案。RGS檔案的具體細節請下載原始碼。

美中不足

    我剛開始實現COMToys的時候,碰到一個小障礙。如果兩個COM介面的方法有相同的名字和簽名怎麼辦?雖然這種情況很少發生,但它確實發生了。例如,IPersistStream 和IPersistFile都有IsDirty。如果像下面這樣寫的話:

DECLARE_IPersistFile();DECLARE_IPersistStream();      

    最後會聲明IsDirty兩次。儘管可以用手工方式進行更正,但這個問題總是讓人有些忐忑不安。用手工來做,你必須重新敲入很多IMPLEMENT_IPersistStream代碼,這太令人討厭。為了避免這種事情,我引入了一個函數級的宏。

 // IPersistFile - 整個介面 IMPLEMENT_IPersistFile(CMyClass,CTPersistFile);  // IPersistStream - 每個單獨的函數 // IMPLEMENT_IPersistStream_IsDirty(); IMPLEMENT_IPersistStream_Load(CMyClass,CTPersistStream); IMPLEMENT_IPersistStream_Save(CMyClass,CTPersistStream); IMPLEMENT_IPersistStream_GetSizeMax(CMyClass,CTPersistStream);      

    這裡再次印證了多繼承(Magic MI Ruler)的優點,只有一個IsDirty,它應用到兩個介面:IPersistFile 和 IPersistStream。這不就是希望的結果嗎?如果你確實要讓兩個介面有相同的函數,又有不同的實現,你完全可以用普通的C++文法來充分限定方法名字:CMyComClass::IPersistStream::IsDirty 和 CMyComClass::IPersistFile::IsDirty。但用這個函數級的宏,至少不用重新敲入——甚至是不用知道它的實現。函數級的宏還提供了為一個或多個介面方法重載標準實現的途徑——儘管如此,我覺得最好還是通過派生自己的實作類別——CTMyPersistStream或別的類來完成,(待續)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.