一年又一年,已經又過了一年了。我VC的生涯已經兩歲了。可以相當地慶賀一下喲。回顧這一年的學習(唉,還沒有工作實踐呢。這年頭,工作不好找哇。),還學了不少的好東西。其中,最重要的就是COM組件模型,我個人覺得這個幾乎是Windows的核心。許多先進的技術(比如微軟著名的DirectX,ADO,沒有人會不知道吧)都以COM組件的形式發布的。現在,我瞄上了另一個好東東,就是泛型程式設計技術。它能夠編寫出清晰、靈活、高度可重用的代碼,在ATL中就可以依稀看出它的影子(現在網上ATL文章有很多,我以後也會談到它)。好了,關於泛型程式設計的事今後再談。
按照我以前的計劃,我應該談談我對COM組件模型的認識了。一來可以對自己的學習狀況進行總結。二來,請教高手,可以幫忙指出錯漏之處。三來,說不定會對初學者們有所協助。請各位高手多多指正啊.在這裡先謝了。
一、動態連結程式庫:
動態鏈態庫是大部分COM組件的承載對象(不要在意ocx,它同樣也是dll,只不過改了一下尾碼而已)。當然Exe同樣也是可以的(TTS中的TextToSpeech對象就是一個例證),只不過在事實上要少得多。
在Windows初期,動態鏈態庫的出現是一場革命。它改變了Windows的一生,也為當今Windows作業系統的霸主地位打下一塊堅實的基石。(關於Windows的曆史問題,我一直沒有弄得太清楚。請VCKBASE的有關史學家們儘快寫出一篇文章來吧^_^)。
微軟對動態連結程式庫就是這樣解釋的:
動態連結程式庫 (DLL) 是作為共用函數庫的可執行檔。動態連結提供了一種方法,使進程可以調用不屬於其可執行代碼的函數。函數的可執行代碼位於一個 DLL 中,該 DLL 包含一個或多個已被編譯、連結並與使用它們的進程分開儲存的函數。DLL 還有助於共用資料和資源。多個應用程式可同時訪問記憶體中單個 DLL 副本的內容。
嗯,講得很清楚。動態連結程式庫首先是一個可執行檔(微軟解釋說,exe叫做起直接可執行檔),它裡麵包含著一組需要共用的函數。當使用時,動態連結程式庫(和Windows系統)會提供一個方法來使我們的應用程式可以調用其中的函數。此外,動態連結程式庫還會包含一些資源(如:表徵圖、對話方塊模板等等)。在MFC中,微軟在現有動態連結程式庫的基礎上施用了一些技巧來提供一些另外功能,如MFC類的匯出。
動態連結程式庫的連結方式大致分為兩類: 靜態連結和動態連結.
靜態連結又叫隱式連結,這種連結方式使我們在代碼中不用語句來指示系統中,我們的應用程式要載入哪些動態連結程式庫。其靜態連結聲明是放在工程屬性中的(或者使用#pragma comment(lib,"XXX.lib"),這個可以和#include放在一起)。在指定時,只需要輸入其動態連結程式庫相應的匯入庫檔案(.lib)。然後,你就可以在程式的任何地方像調用普通函數一樣調用該動態連結程式庫中存在的函數了(當然,你需要包含其相應的標頭檔。一般情況下,標頭檔會和LIB檔案一塊給出)。通過這種方法產生的程式在運行初始化的時候(具體到什麼時候不太清楚。但我可以肯定是在WinMain函數之前了^_^),會自動將動態連結程式庫載入在系統內容中,並將其映射到我們應用程式的進程當中去。當我們調用一個我們進程沒有定義的函數時,VC運行庫會通過尋找LIB檔案的相關資訊找到相應動態連結程式庫的函數並調用它。進程結束時,系統會缷載動態連結程式庫。
動態連結又叫顯式連結,顧名思義這種方式讓我們必需在代碼通過調用API來顯式地載入動態連結程式庫。COM組件模型全部都是採用這種方式來載入進程內組件模組(就是Dll)的。(我覺得微軟的專業術語有些混亂耶)。這個方式有許多好處,它可以在運行時決定具體要載入哪個連結庫,要調用哪個函數…這才叫動態連結呢。
要使用動態連結程式庫並不難,首先要調用LoadLibrary,其原型如下:
HMODULE LoadLibrary(
LPCTSTR lpFileName // file name of module
);
參數lpFileName是要載入的動態連結程式庫的檔案名稱。如果載入成功的話,就返回其控制代碼。否則的,返回NULL。
與這個API相配對的是FreeLibrary,其原型如下:
BOOL FreeLibrary(
HMODULE hModule // handle to DLL module
);
這個就不用我多說了吧。
當動態連結程式庫被LoadLibrary所載入時,C運行庫通過_DllMainCRTStartup來完成動態連結程式庫的初始化,如全域對象(變數)、靜態成員變數的產生以及賦初值。最重要的是它還會調用DllMain函數。每一個動態連結程式庫都必須有這個函數,就像應用程式必須有main或WinMain一樣。它的原型是:
BOOL WINAPI DllMain(
HINSTANCE hinstDLL, // handle to the DLL module
DWORD fdwReason, // reason for calling function
LPVOID lpvReserved // reserved
);