Windows平台下动态链接库的总结
1、 動態鏈接庫與靜態連接庫
? ? ? ?靜態連接庫與動態鏈接庫都是經過編譯器編譯之后的,在計算機上可以直接運行的二進制目標文件,就像exe文件一樣,但不同于exe文件的是靜態鏈接庫和動態鏈接庫不可以獨立運行,一般而言,動態鏈接庫和動態鏈接是在內部實現了一些功能,對外提供了一組接口函數使得外部的程序能夠通過這些對外的接口函數來使用其內部的功能。
? ? ? ?靜態鏈接庫,是在調用該靜態庫的可執行文件編譯的時候(更確切地說是鏈接的時候)就將其中的內容整合進目標文件,而動態鏈接庫則是在運行或加載時才將動態庫中的內容整合進目標文件。
? ? ? ?靜態連接庫命名為XXX.lib,它在程序鏈接時候將被整合進主程序,假如我們要開發一個計算器軟件,該軟件的主程序為Calc.exe,在開發Calc的時候使用到了兩個靜態鏈接庫 add.lib和multi.lib ,其中靜態庫add.lib主要用于對各種類型數據的加、減操作,靜態庫multi.lib主要用于對各種類型數據的進行高效率的乘法和除法操作。在開發Calc.exe的時候,我們就必須在編譯時用到這兩個靜態鏈接庫 add.lib和multi.lib,最終我們發布的程序只有calc.exe,沒有add.lib和multi.lib,因為在生成目標文件calc.exe的時候,編譯工具就已經把這兩個靜態庫中的內容整合進了calc.exe中,這時候用戶拿到的計算器程序就一個文件:Calc.exe。因此靜態庫只是提供給開發人員使用,對于最終用戶來說是不可見的。
? ? ? ?動態鏈接庫一般命名為XXX.dll,它在程序鏈接的時候不會被整合進目標文件,因此在發布應用程序的時候,需要一并把所需的動態庫文件提供給用戶。按照上一個例子,要開發名為Calc.exe的程序,但是在開發的時候用到了兩個動態庫add.dll和multi.dll,其中add.dll主要用于對各種類型數據進行高效的加減操作,add.dll主要用于對各類數據進行高效的乘除操作,在編譯的時候,我們并不需要這兩個dll文件,dll文件是在運行的時候需要的,如
果采用顯示動態加載dll的方式,則只需要一個頭文件就可以了,如果要采用隱式加載時鏈接dll的方式,則還需要對應的lib文件。在Calc的開發過程中,編譯器并沒有將dll中實現的內容放進Calc.exe中,因此我們程序在發布的時候,就不能只發布Calc.exe,還需把Calc.exe所依賴的動態庫add.dll和multi.dll一起提供給用戶,這時候用戶拿到的計算器程序包含了三個文件:Calc.exe、add.dll和multi.dll,其中Calc.exe是可執行程序,用戶只需雙擊它就可以使用。
2、創建動態鏈接庫
? ? ? ?動態庫的開發基本與正常程序開發一樣,只是它不需要main函數,當然dll里面也有個DllMain函數,但是我們一般不使用它。創建動態庫的一般步驟如下:
(1)??創建頭文件,在該頭文件中主要包含待導出的接口函數,該頭文件需和動態庫一起提供給使用者,因為在使用動態庫的時候還需要包含此頭文件,這樣動態庫的使用者才知道動態庫的導出函數是怎么定義的。在windows下動態庫中的函數如果不特殊說明默認是不導出的,因此如果需要導出一個函數,需要在函數的聲明時使用__declspec(dllexport)進行告訴編譯器聲明的函數將被導出。
(2)??創建源文件,在源文件中來實現dll的所有功能。這些源文件不會提供給使用者。
(3)??編譯各源文件,將每個源文件都分別編譯成obj文件。
(4)??鏈接obj文件,連接起將所有的obj文件鏈接起來形成一個dll文件。
(5)??生成lib文件,當動態庫中導出的符號(這里的符號是指導出的函數或變量)超出一個時,還會生成一個lib文件,這里的lib文件不像靜態庫中生成的lib文件(靜態庫的lib文件中包含了靜態庫的全部實現內容),它里面沒有實現代碼,只是包含導出的符號;動態庫的實現代碼被放在dll中,lib文件中只是簡單的包含導出的符號和其他一些鏈接信息,以供隱式使用動態庫所用。
? ? ? ?下面以VS2008為例來創建一個Calc.exe所需要的動態庫add.dll
(1)??File =>New =>Project,在彈出的對話框中依次進入:?????
Visual C++ => Win32 => Win32 ConsoleApplication
在Name中填寫dll工程的名字add,在Location中選擇add功所要保存的路徑,點擊OK,在接下來的一個頁碼中不作修改直接點擊Next,進入Application Settings頁面中選擇DLL(這里默認的是Console application),然后點擊Finish,創建一個空的dll工程。此時可以看到VS已經自動生成了dlmain.cpp,在該文件中就有DllMain的默認定義。這里暫時不做修改。
(2)??添加頭文件add.h,
#ifndef _ADD_H
#ifdef __cplusplus
? #defineMY_EXPORT extern “C”__declspec(dllexport)
#else
? # defineMY_EXPORT __declspec(dllexport)
#endif //__cplusplus
?
MY_EXPORT int add(int iVal1, int iVal2);
#endif //_ADD_H
? ? ? ?這里extern “C”是C和C++混合一起編程時需要使用的修飾符號。另外,編譯器看到__declspec(dllexport)符號時,會為其所修飾的函數、類、變量等生成一些額外的信息,這些額外的信息在使用動態庫的時候會用到。
(3)??添加源文件add.cpp,在源文件中添加導出函數的實現。
#include “add.h”
const int DEFAULT_ADD_VALUE = 100;
int add (int iVal1, int iVal2)
{
? ? ? ?return iVal1 + iVal2 + DEFAULT_ADD_VALUE;
}
(4)??Build 該dll工程,可以在其Debug或Release目錄下看到生成了兩個文件:add.dll和add.lib
在創建動態鏈接庫的時候需要注意:
(1)??避免導出變量,導出變量將不利于動態庫的維護和擴展。
(2)??慎重導出類,因為只有當導出C++類的模塊使用的編譯器和導入C++類的編譯器為同一廠商提供時,才可能保證沒有問題。
3、使用動態庫連接庫
? ? ? ?動態鏈接庫有兩種使用方式:“隱式載入時鏈接”和“顯示運行時鏈接”。“隱式載入時鏈接”方式在程序啟動時就需要把所有依賴的動態庫載入,即時在程序中沒有執行動態庫中的代碼也需要將動態庫加入,如果所依賴的動態庫不存在,則程序無法啟動;“顯示運行時鏈接”方式需要在程序中通過函數LoadLibrary來顯示的加載動態庫,它是運行時才加載所使用的動態庫,如果程序中沒有執行到加載動態庫的操作,則動態庫不會被加載進來;例如:
boolbUseLib = false;
if(bUseLib)
{
?????????? HMODULE hdll = LoadLibrary(“add.dll”);
?????????? Typedef int (*TADD)(int,int);
?????????? TADD add = GetProcAddress(hdll, “add”);
?????????? cout<<add(12,13)<<endl;????
}
? ? ? ?此時,在上面的代碼中動態庫的加載不會被執行,此時即使沒有add.dll程序也可以正常執行。
相反如果在隱式動態庫中,其實現代碼可能為如下形式:
boolbUseLib = false;
if(bUseLib)
{
? ?cout<<add(12,13)<<endl;????
}
? ? ? ?此時,在程序剛啟動時就會加載add.dll,在上述代碼中根本就走add這個分支,但是沒有了add.dll程序還是啟動不了。
在VS2008中,隱式載入時鏈接的配置與使用方式,需要注意的是Debug和Release模式需要分別配置,配置文件也要對應,例如工程的Debug需要配置Debug模式的動態庫,工程的Release模式需要配置Release模式的動態庫:
[1]添加動態庫的頭文件路徑:項目屬性=>C/C++=〉General=〉Additional Include Directories,在其中填入動態庫頭文件的路徑。
[2]配置lib文件的路徑:項目 屬性=>Linker=〉General=〉Additional Library Directories,在其中填入lib文件的路徑。
[3]配置所依賴的lib文件名:項目 屬性=>Linker=〉Input=〉AdditionalDepedencies,在其中填入所依賴的lib文件名稱。
[4]將所依賴的dll文件拷貝到Debug或Release目錄下;或者將dll的路徑添加到環境變量中;或者將dll拷貝到windows下面system32目錄下。
[5]在工程中包含dll的頭文件,然后在程序中就可以直接使用dll中的函數了。
4、可執行程序搜索動態庫的順序
? ? ? ?在運行時,可執行程序將按照下面的順序來搜索動態庫,搜索到所需要的動態庫之后再進行加載,下面以 Calc.exe、add.dll、multi.dll組成的計算器程序在執行時加載動態庫為例(假設這三個文件都放在Calc目錄下):
(1)??可執行程序所在的目錄,如本例中Calc.exe所在的Calc目錄。
(2)??Windows的系統目錄,該目錄可通過函數GetSystemDirectory得到,在xp系統下就是C:\WINDOWS\system32
(3)??Windows的System32子目錄
(4)??Windows目錄,此目錄可以通過函數GetWindowsDirectory得到。
(5)??進程的當前目錄,注意進程的當前目錄不一定是啟動目錄,在使用過程中有可能被更改。
(6)??環境變量path中所列出的目錄。
5、創建和使用動態庫時需要注意的事項
(1)??跨編譯器使用dll時要防止導出符號的改名。假如dll的編譯與使用都是同一家廠商的編譯器,則不存在問題,如果用MS VC++開發的dll要用到其它非MSVC++的編譯器的項目上時,需要采取一些預防措施來防止導出符號被改名,可以通過以下兩種方式來防止導出符號被改名:
[1]在編譯dll的時候增加一個.def文件,并在該文件中添加EXPORT段,然后將導出的符號列在EXPORT之后即可,例如:
EXPORT
??????? add
[2]在編譯dll的時候,在源文件的開始添加類似如下的代碼:
#pragmacomment(linker, ”/export:add=_add@8”)
這里_add@8是MS編譯器自動為符號修改的名字,其修改規則是:在函數名前加”_”,在函數名后jia”@”和傳給函數的參數的字節數構成。
(2)??開發過程中不要在一個動態庫中申請內存然后再這個動態庫之外釋放,這樣不僅容易引起內存泄露,而且還有可能由于申請和釋放內存所使用的運行庫不一樣而引起其它的問題。
(3)??盡量避免導出變量。
(4)??謹慎導出類。
(5)??在顯示動態鏈接的時候可以通過LoadLibrary加載一個exe但是并不執行該exe程序,這樣我們就可以像動態庫一樣使用該exe文件中的資源了,不過此時需要指定動態庫加載函數LoadLibrary的第二個參數Flags為:LOAD_LIBRARY_AS_DATAFILE
(6)??Dll由系統維護,可以在各進程之間共享代碼,因此,顯示動態鏈接時,每調用LoadLibrary加載一次dll,操作系統就會將對應dll的計數器加1,因此一次LoadLibrary就需要對應一次FreeLibrary來將計數器減1,在動態庫加載時可以通過GetModuleHandle來獲取判斷一個動態庫是否被加載,例如:
HMODULEhDll = GetModuleHandle(_T(“add.dll”));
If(NULL==hDll)
??????? hDll = LoadLibrary(_T(“add.dll”));
GetModule還有另外一個作用:返回應用程序的可執行文件的句柄,此時只需傳給它一個NULL參數即可。
總結
以上是生活随笔為你收集整理的Windows平台下动态链接库的总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: _ASSERTE(_CrtIsValid
- 下一篇: PyMongo--非关系型数据库mong