C語言dll文件的說明以及生成、使用方法


  最近在搞一些小項目,由於要涉及到跟其它語言進行交互,動態鏈接庫變成了不二的選擇。為此也查閱了很多資料,將動態鏈接庫的相關知識在此做一個整理。

  一、動態鏈接庫概述

 

  動態鏈接庫(Dynamic Link Library )是一種不可執行的二進制程序文件,它允許多個程序共享執行特殊任務所必需的代碼和其他資源。Windows 中,DLL 多數情況下是帶有 ".dll" 擴展名的文件,但也可能是 ".ocx"或其他擴展名;Linux系統中常常是 ".so" 的文件。動態鏈接提供了一種方法,使進程可以調用不屬於其可執行代碼的函數。函數的可執行代碼位於一個 DLL 文件中,該 DLL 包含一個或多個已被編譯、鏈接並與使用它們的進程分開存儲的函數。DLL 還有助於共享數據和資源。多個應用程序可同時訪問內存中單個 DLL 副本的內容。使用動態鏈接庫可以更為容易地將更新應用於各個模塊,而不會影響該程序的其他部分。是開發大型項目必不可少的部分。

  二、優缺點

  優點:(1)節省內存和代碼重用:當多個程序使用同一個函數庫時,DLL可以減少在磁盤和物理內存中加載代碼的重復量,且有助於代碼的重用。

 

     (2)模塊化:DLL有助於促進模塊式程序開發。模塊化允許僅僅更改幾個應用程序共享使用的一個DLL中的代碼和數據而不需要更改應用程序自身。適用於大規模的軟件開發,使開發過程獨立、耦合度小,便於不同開發者和開發組織之間進行開發和測試。這種模塊化的基本形式允許如Microsoft Office、Microsoft Visual Studio、甚至windows自身這樣大的應用程序使用較為緊湊的補丁和服務包。

       (3)擴展了應用程序的特性,使用dll文件可以使得應用程序能很方便的進行功能的擴展,很多程序的插件機制就是通過dll文件實現的。

     (4)可以用多種語言來編譯和調用,由於各種語言都有自己獨特的開發優勢,在處理某類事務方面具有着獨特的優勢,所以在多種語言編程的過程中,可以利用dll文件作為橋梁,可以發揮多種語言的優點。

 

  缺點:DLL Hell:即DLL地獄,指幾個應用程序在使用同一個共享的DLL庫時發生版本沖突。

 

  究其原因,八個字:成也共用,敗也共用。因為DLL Hell正是由於動態鏈接庫可與其他程序共用函數、資源所導致。

 

  主要有兩種情況

 

  設想這樣一個場景:程序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。

  三、入口點

  就跟應用程序的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函數。

  四、生成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文件,即可使用。

  五、調用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來實現類和變量的共享,還可以實現內存共享,因為研究不多,所以這里暫不介紹。

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM