為了便於學習,本系列文章轉載於http://www.cppblog.com/suiaiguo/archive/2009/07/20/90619.html,如果需要轉載,請註明轉載原網址。
初學DLL,結合教程,總結一下自己的所得,希望對DLL初學者們有所協助。
動態連結程式庫(DLL)是從C語言函數庫和Pascal庫單元的概念發展而來的。所有的C語言標準庫函數都存放在某一函數庫中。在連結應用程式的過程中,連結器從庫檔案中拷貝程式調用的函數代碼,並把這些函數代碼添加到可執行檔中。這種方法同只把函數儲存在已編譯的OBJ檔案中相比更有利於代碼的重用。但隨著Windows這樣的多任務環境的出現,函數庫的方法顯得過於累贅。如果為了完成螢幕輸出、訊息處理、記憶體管理、對話方塊等操作,每個程式都不得不擁有自己的函數,那麼Windows程式將變得非常龐大。Windows的發展要求允許同時啟動並執行幾個程式共用一組函數的單一拷貝。動態連結程式庫就是在這種情況下出現的。動態連結程式庫不用重複編譯或連結,一旦裝入記憶體,DLL函數可以被系統中的任何正在啟動並執行應用程式軟體所使用,而不必再將DLL函數的另一拷貝裝入記憶體。
下面我們一步一步來建立一個DLL。
一、建立一個DLL工程
建立一個工程,選擇Win32 控制台項目(Win32 Console Application),並且在應用程式設定標籤(the advanced tab)上,選擇DLL和空項目選項。
二、聲明匯出函數
這裡有兩種方法聲明匯出函數:一種是通過使用__declspec(dllexport),添加到需要匯出的函數前,進行聲明;另外一種就是通過模組定義檔案(Module-Definition File即.DEF)來進行聲明。
第一種方法,建立標頭檔DLLSample.h,在標頭檔中,對需要匯出的函數進行聲明。
#ifndef _DLL_SAMPLE_H
#define _DLL_SAMPLE_H
// 如果定義了C++編譯器,那麼聲明為C連結方式
#ifdef __cplusplus
extern "C" {
#endif
// 通過宏來控制是匯入還是匯出
#ifdef _DLL_SAMPLE
#define DLL_SAMPLE_API __declspec(dllexport)
#else
#define DLL_SAMPLE_API __declspec(dllimport)
#endif
// 匯出/匯入函式宣告
DLL_SAMPLE_API void TestDLL(int);
#undef DLL_SAMPLE_API
#ifdef __cplusplus
}
#endif
#endif
這個標頭檔會分別被DLL和調用DLL的應用程式引入,當被DLL引入時,在DLL中定義_DLL_SAMPLE宏,這樣就會在DLL模組中聲明函數為匯出函數;當被調用DLL的應用程式引入時,就沒有定義_DLL_SAMPLE,這樣就會聲明標頭檔中的函數為從DLL中的匯入函數。
第二種方法:模組定義檔案是一個有著.def副檔名的文字檔。它被用於匯出一個DLL的函數,和__declspec(dllexport)很相似,但是.def檔案並不是Microsoft定義的。一個.def檔案中只有兩個必需的部分:LIBRARY 和 EXPORTS。
LIBRARY DLLSample
DESCRIPTION "my simple DLL"
EXPORTS
TestDLL @1 ;@1表示這是第一個匯出函數
第一行,''LIBRARY''是一個必需的部分。它告訴連結器(linker)如何命名你的DLL。下面被標識為''DESCRIPTION''的部分並不是必需的。該語句將字串寫入 .rdata 節,它告訴人們誰可能使用這個DLL,這個DLL做什麼或它為了什麼(存在)。再下面的部分標識為''EXPORTS''是另一個必需的部分;這個部分使得該函數可以被其它應用程式訪問到並且它建立一個匯入庫。當你產生這個項目時,不僅是一個.dll檔案被建立,而且一個副檔名為.lib的匯出庫也被建立了。除了前面的部分以外,這裡還有其它四個部分標識為:NAME,
STACKSIZE, SECTIONS, 和 VERSION。另外,一個分號(;)開始一個註解,如同''//''在C++中一樣。定義了這個檔案之後,標頭檔中的__declspec(dllexport)就不需要聲明了。
三、編寫DllMain函數和匯出函數
DllMain函數是DLL模組的預設進入點。當Windows載入DLL模組時調用這一函數。系統首先調用全域對象的建構函式,然後調用全域函數DLLMain。DLLMain函數不僅在將DLL連結載入到進程時被調用,在DLL模組與進程分離時(以及其它時候)也被調用。
#include "stdafx.h"
#define _DLL_SAMPLE
#ifndef _DLL_SAMPLE_H
#include "DLLSample.h"
#endif
#include "stdio.h"
//APIENTRY聲明DLL函數進入點
BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
void TestDLL(int arg)
{
printf("DLL output arg %d\n", arg);
}
如果程式員沒有為DLL模組編寫一個DLLMain函數,系統會從其它運行庫中引入一個不做任何操作的預設DLLMain函數版本。在單個線程啟動和終止時,DLLMain函數也被調用。
然後,F7編譯,就得到一個DLL了。