1. 程式人生 > >.lib .dll 區別介紹、使用(dll的兩種引入方式)

.lib .dll 區別介紹、使用(dll的兩種引入方式)

.lib .dll檔案都是程式可直接引用的檔案,前者就是所謂的庫檔案,後者是動態連結庫(Dynamic Link Library)也是一個庫檔案。而.pdb則可以理解為符號表檔案。DLL(Dynamic Link Library)檔案為動態連結庫檔案,又稱為“應用程式擴充套件”,是一種軟體檔案型別。在Windows中,許多應用程式並不是一個完整的可執行檔案,它們被分割成一些相對獨立的動態連結庫,即dll檔案,放置於系統中。

關於lib和dll的區別

(1)lib是編譯時用到的,dll是執行時用到的。如果要完成原始碼的編譯,只需要lib;如果要使動態連結的程式執行起來,只需要dll。
(2)如果有dll檔案,那麼lib一般是一些索引資訊,記錄了dll中函式的入口和位置,dll中是函式的具體內容;如果只有lib檔案,那麼這個lib檔案是靜態編譯出來的,索引和實現都在其中。使用靜態編譯的lib檔案,在執行程式時不需要再掛動態庫,缺點是導致應用程式比較大,而且失去了動態庫的靈活性,釋出新版本時要釋出新的應用程式才行。
(3)動態連結的情況下,有兩個檔案:一個是LIB檔案,一個是DLL檔案。LIB包含被DLL匯出的函式名稱和位置,DLL包含實際的函式和資料,應用程式使用LIB檔案連結到DLL檔案。在應用程式的可執行檔案中,存放的不是被呼叫的函式程式碼,而是DLL中相應函式程式碼的地址,從而節省了記憶體資源。如果不想用lib檔案或者沒有lib檔案,可以用WIN32 API函式LoadLibrary、GetProcAddress裝載。

需要的檔案

靜態連結庫(Static Link Library)使用lib需注意兩個檔案:

  1. h標頭檔案,包含lib中說明輸出的類或符號原型或資料結構。應用程式呼叫lib時,需要將該檔案包含入應用程式的原始檔中。
  2. LIB檔案,這種 lib 中有函式的實現程式碼,它是將 lib 中的程式碼加入目標模組(.exe 或者 .dll)檔案中,所以連結好了之後,lib 檔案就沒有用了。這種 lib檔案實際上是任意個 obj 檔案的集合。obj 檔案則是 cpp 檔案編譯生成的,如果有多個cpp 檔案則會編譯生成多個 obj 檔案,從而生成的 lib 檔案中也包含了多個 obj。

動態連結庫(Dynamic Link Library)的匯入庫(Import Library)使用dll需注意三個檔案:

  1. h標頭檔案,包含dll中說明輸出的類或符號原型或資料結構的.h檔案。應用程式呼叫dll時,需要將該檔案包含入應用程式的原始檔中。
  2. LIB檔案,是dll在編譯、連結成功之後生成的檔案,作用是當其他應用程式呼叫dll時,需要將該檔案引入應用 程式,否則產生錯誤。如果不想用lib檔案或者沒有lib檔案,可以用WIN32 API函式LoadLibrary、GetProcAddress裝載。
  3. dll檔案,真正的可執行檔案,開發成功後的應用程式在釋出時,只需要有.exe檔案和.dll檔案,並不需要.lib檔案和.h標頭檔案。

這種 lib 是和 dll 配合使用的,裡面沒有程式碼,程式碼在 dll 中,這種 lib 是用在靜態呼叫 dll 上的,所以起的作用也是連結作用,連結完成了, lib 也沒用了。至於動態呼叫 dll 的話,根本用不上 lib 檔案。目標模組(exe 或者 dll)檔案生成之後,就用不著 lib 檔案了。

引入載入檔案

載入lib/標頭檔案(若不新增,連線報錯:無法解析的外部符號)

靜態連結只需要lib檔案,當然標頭檔案也是需要的。這種方式時候lib檔案中有兩個部分,可以將檔案字尾改為rar解壓可以看到。第一部分就是和第一種方式中的key,第二部分是.obj檔案儲存在obj資料夾下,他相當與dll中的機器碼,只不過這個機器碼是在連結的時候放入程式的,而不是在程式執行時拿進來的。如果這時候我們也有原始碼,並且希望IED可以除錯原始碼,這很容易因為如果lib檔案生成的時候模式是NDEBUG(好像不是也可以),obj資料夾下是有一個xx.pdb這個pdb檔案中的東西會被IED放入程式的pdb中,所以直接指定原始碼位置使用就可以了。
1 第一步:專案->屬性->C/C++->常規->附加包含目錄(瀏覽.h檔案的路徑)   新增包含檔案
2 第二步:專案->屬性->C/C++->連結器->輸入->附加依賴項(寫入lib的名稱) 新增用到的lib (這一步也可以在程式碼中顯示呼叫 #pragma comment(lib, “***.lib”) )
    專案->屬性->C/C++->連結器->常規->附加庫目錄        新增庫檔案路徑
4 第三步:在要使用cpp檔案前加入 #include <gtest/gtest.h>(注意檔案的路徑)
上述方法,在每次建立工程時都要重新進行設定,而且debug和release都要進行設定,同時注意win32和win64平臺。

載入dll(如果不新增,編譯連結不報錯,執行報錯:無法找到***.dll)

這種方式的基本原理是lib檔案中包擴了某一段程式(函式)的入口或者說是地址,而他真正的機器碼是在dll檔案中,IDE連結的時候將.lib檔案(程式地址)連結到原始碼中,程式執行時到相應位置(環境變數path,當前目錄等)尋找dll檔案並執行其中的機器碼。所以這種引用方式一般需要的檔案一般有三個:.h,.lib,.dll,生成的源程式也會比較小,因為他只儲存了函式地址,但是這種方式總是會出現找不到xxx.dll這種問題。如果這時候我們還有dll的原始碼,並且希望IED可以除錯原始碼,那麼就需要.pdb檔案了,pdb檔案中儲存了dll的符號表,所謂符號表可以理解為機器碼(這裡是dll中的)中插入的key與原始碼檔案的對映,這樣只要指定原始碼存放的路徑,IDE就會自動去找原始碼。需要注意的是,pdb檔案和dll檔案是配套的,也就是說一旦dll檔案有改動(比如說重新生成)pdb檔案就必須做相應改變。pdb檔案也比較大,程式執行時也會因為要完成對映而比較慢,這也是release版與debug的區別。

對於dll檔案的使用,將dll檔案拷貝到工程debug檔案下,或者在系統環境變數中加入dll檔案的路徑(...\bin)。

(1)附加依賴項配置

類似靜態庫的使用,直接在vs中進行配置。(其實本質還是隱式連結,只是配置方式不同)

(2)隱式連結

第一種方法是:通過project->link->Object/LibraryModule中加入.lib檔案(或者在原始碼中加入指令#pragma comment(lib, “Lib.lib”)),並將.dll檔案置入工程所在目錄,然後新增對應的.h標頭檔案。

 1 #include "stdafx.h"
 2 #include "DLLSample.h"
 3 
 4 #pragma comment(lib, "DLLSample.lib") //你也可以在專案屬性中設定庫的連結
 5 
 6 int main()
 7 {
 8     TestDLL(123); //dll中的函式,在DllSample.h中宣告
 9     return(1);
10 }

(3)顯式連結

還有一種方式是呼叫windows的api LoadLibrary來載入dll,並根據標頭檔案呼叫GetProcAddress 載入dll中的函式,最後使用FreeLibrary釋放,這種方式顯然不可以除錯。需要函式指標和WIN32 API函式LoadLibrary、GetProcAddress裝載,使用這種載入方法,不需要.lib檔案和.h標頭檔案,只需要.dll檔案即可(將.dll檔案置入工程目錄中)。

 1 #include <iostream>
 2 #include <windows.h> //使用函式和某些特殊變數
 3 typedef void (*DLLFunc)(int);
 4 int main()
 5 {
 6     DLLFunc dllFunc;
 7     HINSTANCE hInstLibrary = LoadLibrary("DLLSample.dll");
 8 
 9     if (hInstLibrary == NULL)
10     {
11         FreeLibrary(hInstLibrary);
12     }
13     dllFunc = (DLLFunc)GetProcAddress(hInstLibrary, "TestDLL");
14     if (dllFunc == NULL)
15     {
16         FreeLibrary(hInstLibrary);
17     }
18     dllFunc(123);
19     std::cin.get();
20     FreeLibrary(hInstLibrary);
21     return(1);
22 }

LoadLibrary函式利用一個名稱作為引數,獲得DLL的例項(HINSTANCE型別是例項的控制代碼),通常呼叫該函式後需要檢視一下函式返回是否成功,如果不成功則返回NULL(控制代碼無效),此時呼叫函式FreeLibrary釋放DLL獲得的記憶體。
GetProcAddress函式利用DLL的控制代碼和函式的名稱作為引數,返回相應的函式指標,同時必須使用強轉;判斷函式指標是否為NULL,如果是則呼叫函式FreeLibrary釋放DLL獲得的記憶體。此後,可以使用函式指標來呼叫實際的函式。
最後要記得使用FreeLibrary函式釋放記憶體。

注意:應用程式如何找到DLL檔案?

使用LoadLibrary顯式連結,那麼在函式的引數中可以指定DLL檔案的完整路徑;如果不指定路徑,或者進行隱式連結,Windows將遵循下面的搜尋順序來定位DLL:
(1)包含EXE檔案的目錄
(2)工程目錄
(3)Windows系統目錄
(4)Windows目錄
(5)列在Path環境變數中的一系列