鏈接庫是以二進制方式存儲數(shù)據(jù)的磁盤文件,本身不能運行,需要其他的應(yīng)用程序執(zhí)行時動態(tài)加載或在編譯前進行引用。它分靜態(tài)鏈接庫和動態(tài)鏈接庫兩種。
(1) 靜態(tài)鏈接庫(Static-Link Library)
靜態(tài)鏈接庫是一些相對較小的、比較穩(wěn)定的函數(shù)庫。例如C語言的標準函數(shù)庫stdlib.lib。它通常是幾組與應(yīng)用程序相鏈接的可重用的函數(shù),作為應(yīng)用程序的一部分,如果其內(nèi)容有所更新,必須重新鏈接。
(2) 動態(tài)鏈接庫(Dynamic-Link Library)
動態(tài)鏈接庫可以在需要時動態(tài)加載,其在內(nèi)存中只有一個實例,如果一個應(yīng)用程序調(diào)用了DLL,那么后面其他的應(yīng)用程序再調(diào)用時,只是將DLL的地址映射到自己的進程地址空間中(16位的系統(tǒng)時DLL是被加載到共享地址空間,32位系統(tǒng)則是采用地址映射的方式)。對于每一個DLL,系統(tǒng)維持一個計數(shù)器,它記錄當前有多少個應(yīng)用程序在使用該DLL。一旦該計數(shù)器減為0,系統(tǒng)就會將該DLL從內(nèi)存中卸載掉。由于DLL是可以被動態(tài)加載的,所以可以將應(yīng)用程序的某些模塊編譯到DLL中,這樣可以減少應(yīng)用程序的體積,而且在以后維護時,只需更新相應(yīng)的DLL即可,無需重新編譯應(yīng)用程序。
DLL的結(jié)構(gòu)和應(yīng)用程序很相近,每個應(yīng)用程序都已一個入口函數(shù)WinMain,每個DLL也有一個入口函數(shù)DllMain;應(yīng)用程序中含有資源,DLL也可含有資源;DLL和應(yīng)用程序一樣有自己的數(shù)據(jù)段和代碼段,也可使用回調(diào)(callback)函數(shù),也可自定義消息。不同的主要是DLL有輸入符號表和輸出符號表,以方便應(yīng)用程序調(diào)用DLL中的函數(shù)。DLL中的入口函數(shù)DllMain的主要框架如下:
BOOL APIENTRY DllMain(HANDLE hModule, // 動態(tài)鏈接庫被調(diào)用時一個指向自己的句柄
DWORD ul_reason_for_call, // 動態(tài)鏈接庫被調(diào)用的原因
LPVOID lpReserved // 系統(tǒng)保留 )
{
switch(ul_reason_for_call)
{
case DLL_PROCESS_ATTACH : // 進程被調(diào)用
case DLL_THREAD_ATTACH : // 線程被調(diào)用
case DLL_THREAD_DETACH : // 線程被停止
case DLL_PROCESS_DETACH : // 進程被停止
return TRUE;
}
}
1、靜態(tài)鏈接庫的創(chuàng)建及使用
(1)靜態(tài)鏈接庫的創(chuàng)建
VC++提供創(chuàng)建向?qū)?,運行菜單“File/new”,進入工程向?qū)нx項,對話框列表中選定“Win32 Static Libraby”。
工程向?qū)е饕O(shè)置Pre-Compiled header和MFC support兩個選項。
Pre-Compiled header復(fù)選框:選中的話,向?qū)г诠こ讨袝蓅tdafx.h和stdafx.cpp兩個文件;
MFC support復(fù)選框:選中的話,向?qū)趕tdafx.h中include afx.h和afxwin.h兩個頭文件,如果第一個不選擇的話,此復(fù)選框無意義;
如果連個都不選擇的話,向?qū)?chuàng)建一個空的工程;
然后用戶自己動手添加相應(yīng)的功能實現(xiàn)文件,完成后經(jīng)過編譯生成LIB文件。
(2)靜態(tài)鏈接庫的使用
有兩種方法:一種是在VC的“工程”>>“設(shè)置”里面設(shè)定;一種是編寫代碼設(shè)定。
第一種:打開“projects”>>"settings",選擇“Link”選項卡,在“Objects/library modules”輸入需要使用的靜態(tài)鏈接庫文件,例如“static.lib"。如有多個靜態(tài)鏈接庫文件時,中間要用逗號隔開。
第二種:在要使用到靜態(tài)鏈接庫的.cpp文件的起始部分輸入以下代碼:
include "static.h" // 添加對鏈接庫頭文件的包含,注意:不管是第一種還是第二種方法,該頭文件必須包含
#pragma comment(lib, "static.lib") // 添加對鏈接庫的調(diào)用,注意:該行代碼是在編譯階段完成靜態(tài)鏈接庫的鏈接
2、動態(tài)鏈接庫的創(chuàng)建和使用
動態(tài)鏈接庫VC工程向?qū)峁┝藘煞N創(chuàng)建向?qū)?,一個是”Win32 Dynamic-Link Library“工程向?qū)?,一個是”MFC AppWizard(dll)“工程向?qū)А?/p>
(1)Win32 Dynamic-Link Library的創(chuàng)建
Win32動態(tài)鏈接庫的創(chuàng)建需要用戶設(shè)置工程類型:
An empty DLL project——不帶任何文件的空工程;
A simple DLL project——創(chuàng)建一個動態(tài)鏈接庫工程,該工程有三個文件:StdAfx.cpp和StdAfx.h,以及一個帶有DllMain函數(shù)實現(xiàn)文件;
A DLL that exports some symbols——創(chuàng)建一個可以導(dǎo)出類的動態(tài)鏈接庫工程;
(2)MFC AppWizard(dll)創(chuàng)建
MFC AppWizard(dll)工程向?qū)婕跋旅?種選擇方式:
Regular DLL with MFC statically linked——以靜態(tài)調(diào)用MFC庫的方式創(chuàng)建正規(guī)的DLL,這樣創(chuàng)建的DLL體積會很大,使用時可以沒有MFC庫(mfc42.dll等一些文件);
Regular DLL using shared MFC DLL——以動態(tài)調(diào)用MFC庫的方式創(chuàng)建正規(guī)的DLL,這樣創(chuàng)建的DLL體積很小,使用時系統(tǒng)中必須有MFC庫;
MFC Extension DLL(using share MFC DLL)——運用動態(tài)鏈接的方式創(chuàng)建擴展的DLL,它能夠支持C++接口,可以像類一樣使用,即可以派生和繼承。這樣生成的DLL文件比動態(tài)方式創(chuàng)建的正規(guī)的DLL還小。
(注意:正規(guī)的DLL的兩種調(diào)用方式可以通過”project/settings“設(shè)置對話框中General選項卡的”Microsoft Foundation Classes“列表框中選擇如何引用MFC的動態(tài)庫來進行相互轉(zhuǎn)變。)
擴展DLL和正規(guī)DLL區(qū)別:
正規(guī)DLL既可以被MFC庫的應(yīng)用程序加載,也可被非MFC庫的應(yīng)用程序加載;而擴展只能動態(tài)鏈接到MFC庫,而且也只能在使用MFC庫的應(yīng)用程序中使用;
擴展DLL中有明顯動態(tài)鏈接庫的入口函數(shù)DllMain,而規(guī)則DLL則是由編譯器隱式創(chuàng)建;
擴展DLL沒有自己的模塊狀態(tài),它總是使用客戶程序的模塊狀態(tài),而使用規(guī)則DLL時需要設(shè)置模塊狀態(tài),其設(shè)置語句:AFX_MANAGE_STATE(&afxModuleState)
動態(tài)鏈接庫的函數(shù)需要導(dǎo)出供應(yīng)用程序調(diào)用。導(dǎo)出時可以使用命名改編和DEF文件兩種方式。使用命名改編也可以將鏈接庫中類導(dǎo)出。
命名改編:
方法:在需要導(dǎo)出的函數(shù)前面增加extern "C" __declspec(dllexport)聲明。
其實只增加__declspec(dllexport)就可導(dǎo)出函數(shù),但是如果沒有加上extern "C",就只能使用隱式調(diào)用了(##pragma comment(lib, "Dynic.lib"),因為此時導(dǎo)出函數(shù)名稱發(fā)生變化。(注意:extern "C"中的'C'一定要大寫)名稱發(fā)生改變時因為__declspec是一種命名和調(diào)用約定,與此類似的還有_cdecl、_stdcall、_fastcall和thiscall。采用不同的命名和調(diào)用約定,導(dǎo)出的函數(shù)名與原函數(shù)名就會不同,extern "C"就是限制在導(dǎo)出函數(shù)時不改變原函數(shù)的函數(shù)名。
DEF文件:
使用模塊定義文件導(dǎo)出函數(shù)時,導(dǎo)出的函數(shù)名稱不會發(fā)生改變,而且也不需使用extern "C" __declspec(dllexport)聲明函數(shù)。下面是一個簡單DEF文件的內(nèi)容(只涉及到DLL導(dǎo)出):
; ResourseDll.def : Declares the module parameters for the DLL.
LIBRARY "ResourseDll"
DESCRIPTION 'ResourseDll Windows Dynamic Link Library'
EXPORTS
; Explicit exports can go here
func1@1
func2@2
LIBRARY:動態(tài)鏈接庫名稱模塊,LIBRARY后面的字符就是動態(tài)鏈接庫名稱;
DESCRIPTION:動態(tài)鏈接庫描述模塊,LIBRARY后面的字符就是對動態(tài)鏈接庫的描述;
EXPORTS:函數(shù)導(dǎo)出模塊,其后是導(dǎo)出函數(shù),定義導(dǎo)出函數(shù)的語法例如"func1@1“,func1是函數(shù)名,@后面的數(shù)字表示函數(shù)在動態(tài)鏈接庫中的導(dǎo)出序號;
";":注釋符號,分號后面的就是注釋文本
(3)動態(tài)鏈接庫的使用
動態(tài)鏈接庫的使用分為隱式調(diào)用和動態(tài)加載兩種。
隱式調(diào)用:
在需要使用鏈接庫的.cpp文件起始部分添加下列代碼:
include "static.h" // 添加對鏈接庫頭文件的包含
#pragma comment(lib, "Dynic.lib") // 添加對鏈接庫的調(diào)用,注意:該行代碼是在編譯階段完成對鏈接庫的鏈接
上面的第2行代碼也可通過"project/settings"對話框進行設(shè)置(前面靜態(tài)庫使用中介紹過)
(注意:此時必須把DLL文件和LIB文件一起拷到應(yīng)用程序的目錄下,缺一不可,這是因為動態(tài)鏈接庫與靜態(tài)鏈接庫不同,靜態(tài)鏈接庫把函數(shù)實現(xiàn)放在LIB文件中,所以只需要LIB文件即可,而動態(tài)鏈接庫的函數(shù)實現(xiàn)放在DLL文件中,LIB文件只是存放函數(shù)入口地址)
動態(tài)加載:
動態(tài)加載主要通過LoadLibrary、GetProcAddress和FreeLibrary三個函數(shù)實現(xiàn)。
HINSTANCE LoadLibrary(
LPCTSTR lpLibFileName // 要裝載的DLL或可執(zhí)行程序的文件名,可以是相對路徑,也可是絕對路徑
);
該函數(shù)用來裝載模塊(動態(tài)連接庫和可執(zhí)行程序都是模塊),函數(shù)執(zhí)行成功返回模塊句柄。
FARPROC GetProcAddress(
HMODULE hModule, // 加載到內(nèi)存的DLL模塊句柄,主要指LoadLibrary函數(shù)的返回值
LPCSTR lpProcName // 函數(shù)的名稱
);
該函數(shù)如果執(zhí)行成功,返回模塊中具體函數(shù)的指針。
BOOL FreeLibrary(
HMODULE hLibModule // 裝載在內(nèi)存中的DLL模塊句柄
);
該函數(shù)用來釋放DLL模塊所占用的內(nèi)存,主要與LoadLibrary函數(shù)配套使用。
(4)動態(tài)鏈接庫導(dǎo)出類
要想能夠從動態(tài)鏈接庫中導(dǎo)出C++類,關(guān)鍵在于在該類的類名和class關(guān)鍵字中間插入語句__declspec(dllexport)。如果只想導(dǎo)出類的某個成員函數(shù),則只需要在該成員函數(shù)的返回類型和函數(shù)名中間添加__declspec(dllexport)即可。使用__declspec(dllexport)語句的成員函數(shù)可以像全局函數(shù)一樣被調(diào)用。
動態(tài)鏈接庫中類可以和應(yīng)用程序中類一樣使用,但是需要而且只能使用隱式調(diào)用的方式來加載動態(tài)鏈接庫。
導(dǎo)出整個C++類:
class __declspec(dllexport)CClass
{
public:
int Func1(int a, int b);
int Func2(int a, int b);
};
導(dǎo)出類中某個成員函數(shù):
class CClass
{
public:
int Func1(int a, int b);
int __declspec(dllexport)Func2(int a, int b);
};