最近在搞一些小項目,因爲要涉及到跟其它語言進行交互,動態連接庫變成了不二的選擇。爲此也查閱了不少資料,將動態連接庫的相關知識在此作一個整理。ios
1、動態連接庫概述編程
動態連接庫(Dynamic Link Library )是一種不可執行的二進制程序文件,它容許多個程序共享執行特殊任務所必需的代碼和其餘資源。Windows 中,DLL 多數狀況下是帶有 ".dll" 擴展名的文件,但也多是 ".ocx"或其餘擴展名;Linux系統中經常是 ".so" 的文件。動態連接提供了一種方法,使進程能夠調用不屬於其可執行代碼的函數。函數的可執行代碼位於一個 DLL 文件中,該 DLL 包含一個或多個已被編譯、連接並與使用它們的進程分開存儲的函數。DLL 還有助於共享數據和資源。多個應用程序可同時訪問內存中單個 DLL 副本的內容。使用動態連接庫能夠更爲容易地將更新應用於各個模塊,而不會影響該程序的其餘部分。是開發大型項目必不可少的部分。windows
2、優缺點模塊化
優勢:(1)節省內存和代碼重用:當多個程序使用同一個函數庫時,DLL能夠減小在磁盤和物理內存中加載代碼的重複量,且有助於代碼的重用。函數
(2)模塊化:DLL有助於促進模塊式程序開發。模塊化容許僅僅更改幾個應用程序共享使用的一個DLL中的代碼和數據而不須要更改應用程序自身。適用於大規模的軟件開發,使開發過程獨立、耦合度小,便於不一樣開發者和開發組織之間進行開發和測試。這種模塊化的基本形式容許如Microsoft Office、Microsoft Visual Studio、甚至windows自身這樣大的應用程序使用較爲緊湊的補丁和服務包。測試
(3)擴展了應用程序的特性,使用dll文件可使得應用程序能很方便的進行功能的擴展,不少程序的插件機制就是經過dll文件實現的。spa
(4)能夠用多種語言來編譯和調用,因爲各類語言都有本身獨特的開發優點,在處理某類事務方面具備着獨特的優點,因此在多種語言編程的過程當中,能夠利用dll文件做爲橋樑,能夠發揮多種語言的優勢。插件
缺點:DLL Hell:即DLL地獄,指幾個應用程序在使用同一個共享的DLL庫時發生版本衝突。線程
究其緣由,八個字:成也共用,敗也共用。由於DLL Hell正是因爲動態連接庫可與其餘程序共用函數、資源所致使。debug
主要有兩種狀況:
設想這樣一個場景:程序A會使用1.0版本的動態連接庫X,則在程序A安裝到系統時,會同時安裝該1.0版本的動態連接庫X。假設另外一個程序B也會使用到動態連接庫X,那麼程序B直接複製到硬盤中便可正常運行,由於動態連接庫已經存在於系統中。然而有一天,另外一程序C也要使用動態連接庫X,可是因爲程序C開發的時間較晚,其須要較新版本---2.0版本的動態連接庫X。則在程序C被安裝到系統時,2.0版本的動態連接庫X 也必須隨之安裝到系統中,此時系統中1.0版本的動態連接庫將被2.0版本所取代(替換)。
狀況1:新版本的動態連接庫不兼容舊版本。如,A何B須要X所提供的功能,在升級到2.0後,新版本的X居然把此功能取消了(很難想象吧,呵呵但有時候就是如此....)。則此時雖然C能正常運行,但A和B均沒法工做了。
狀況2:新版本的動態連接庫兼容舊版本,可是存在一個bug。
3、入口點
就跟應用程序的main函數同樣,dll文件也有入口函數,叫作DllMain(),它的原型是這樣的:
1 BOOL APIENTRY DllMain( 2 HANDLE hModule, // DLL模塊的句柄 3 DWORD ul_reason_for_call, // 調用本函數的緣由 4 LPVOID lpReserved // 保留 5 ) { 6 switch (ul_reason_for_call) 7 { 8 case DLL_PROCESS_ATTACH: 9 //進程正在加載本DLL 10 break; 11 case DLL_THREAD_ATTACH: 12 //一個線程被建立 13 break; 14 case DLL_THREAD_DETACH: 15 //一個線程正常退出 16 break; 17 case DLL_PROCESS_DETACH: 18 //進程正在卸載本DLL 19 break; 20 } 21 return TRUE; //返回TRUE,表示成功執行本函數 22 }
入口點函數只應執行簡單的初始化任務,不該調用任何其餘 DLL 加載函數或終止函數。例如,在入口點函數中,不該直接或間接調用 LoadLibrary 函數或LoadLibraryEx 函數。此外,不該在進程終止時調用 FreeLibrary函數。
4、生成DLL文件
下面來生成一個DLL文件,爲方便起見,只定義一個簡單函數。
生成DLL文件須要用到兩個文件,一個頭文件,dll_add.h,和一個源文件,dll_add.c
頭文件內容:
1 #ifndef _DLL_DEMO_H_ 2 #define _DLL_DEMO_H_ 3 #ifdef DLLDEMO_EXPORTS 4 #define DLL_DEMO _declspec( dllexport ) 5 #else 6 #define DLL_DEMO _declspec(dllimport) 7 #endif 8 extern "C" DLL_DEMO int Add(int a, int b); 9 #endif
源文件內容:
1 #include "dll_demo.h" 2 3 int Add(int a, int b) 4 { 5 return (a + b); 6 }
這裏由於不須要對函數載入與卸載做特殊處理,因此能夠不使用入口函數。
使用的是vs2015,在debug模式或者release模式下調試後會在相應目錄下生成dll文件,便可使用。
5、調用DLL文件
生成DLL天然是爲了調用,調用DLL有兩種方式。
靜態調用:使用.h+.lib+.dll
1 #include <windows.h> 2 #include <iostream> 3 #include "DLL_DEMO.h" 4 using namespace std; 5 #pragma comment(lib, "DLL_DEMO.lib") 6 7 extern "C" _declspec(dllimport) int Add(int a, int b); 8 int main(int argc, char *argv[]) 9 { 10 cout << Add(2, 3) << endl; 11 system("pause"); 12 return 0; 13 }
把頭文件和lib文件、dll文件都放到跟源文件同一目錄下便可使用。固然,路徑能夠從新設置。
動態調用:僅使用dll文件
1 #include <windows.h> 2 #include <iostream> 3 using namespace std; 4 typedef int (*AddFunc)(int a, int b); 5 int main(int argc, char *argv[]) 6 { 7 HMODULE hDll = LoadLibrary(L"DLL_DEMO.dll"); 8 if (hDll != NULL) 9 { 10 AddFunc add = (AddFunc)GetProcAddress(hDll, "Add"); 11 if (add != NULL) 12 { 13 cout<<add(2, 3)<<endl; 14 } 15 FreeLibrary(hDll); 16 } 17 }
在字符串前加一個L做用: unicode字符集是兩個字節組成的。L告示編譯器使用兩個字節的 unicode 字符集。
也可使用dll來實現類和變量的共享,還能夠實現內存共享,由於研究很少,因此這裏暫不介紹。