1. 程式人生 > >lib檔案 與 dll 檔案

lib檔案 與 dll 檔案

什麼是lib檔案,lib和dll的關係如何   
(1)lib是編譯時需要的,dll是執行時需要的。 
如果要完成原始碼的編譯,有lib就夠了。 
如果也使動態連線的程式執行起來,有dll就夠了。 
在開發和除錯階段,當然最好都有。 
(2)一般的動態庫程式有lib檔案和dll檔案。lib檔案是必須在編譯期就連線到應用程式中的,而dll檔案是執行期才會被呼叫的。如果有dll檔案,那麼對應的lib檔案一般是一些索引資訊,具體的實現在dll檔案中。如果只有lib檔案,那麼這個lib檔案是靜態編譯出來的,索引和實現都在其中。靜態編譯的lib檔案有好處:給使用者安裝時就不需要再掛動態庫了。但也有缺點,就是導致應用程式比較大,而且失去了動態庫的靈活性,在版本升級時,同時要釋出新的應用程式才行。 

(3)在動態庫的情況下,有兩個檔案,一個是引入庫(.LIB)檔案,一個是DLL檔案,引入庫檔案包含被DLL匯出的函式的名稱和位置,DLL包含實際的函式和資料,應用程式使用LIB檔案連結到所需要使用的DLL檔案,庫中的函式和資料並不複製到可執行檔案中,因此在應用程式的可執行檔案中,存放的不是被呼叫的函式程式碼,而是DLL中所要呼叫的函式的記憶體地址,這樣當一個或多個應用程式執行是再把程式程式碼和被呼叫的函式程式碼連結起來,從而節省了記憶體資源。從上面的說明可以看出,DLL和.LIB檔案必須隨應用程式一起發行,否則應用程式將會產生錯誤。 
一、開發和使用dll需注意三種檔案     
  1、   dll標頭檔案     

  它是指dll中說明輸出的類或符號原型或資料結構的.h檔案。當其它應用程式呼叫dll時,需要將該檔案包含入應用程式的原始檔中。     
  2、   dll的引入庫檔案     
  它是dll在編譯、連結成功後生成的檔案。主要作用是當其它應用程式呼叫dll時,需要將該檔案引入應用程式。否則,dll無法引入。     
  3、   dll檔案(.dll)     
  它是應用程式呼叫dll執行時,真正的可執行檔案。dll應用在編譯、連結成功後,.dll檔案即存在。開發成功後的應用程式在釋出時,只需要有.exe檔案和.dll檔案,不必有.lib檔案和dll標頭檔案。    
動態連結庫 (DLL) 是作為共享函式庫的可執行檔案。動態連結提供了一種方法,使程序可以呼叫不屬於其可執行程式碼的函式。函式的可執行程式碼位於一個 DLL 中,該 DLL 包含一個或多個已被編譯、連結並與使用它們的程序分開儲存的函式。DLL 還有助於共享資料和資源。多個應用程式可同時訪問記憶體中單個 DLL 副本的內容。  

動態連結與靜態連結的不同之處在於:動態連結允許可執行模組(.dll 檔案或 .exe 檔案)僅包含在執行時定位 DLL 函式的可執行程式碼所需的資訊。在靜態連結中,連結器從靜態連結庫獲取所有被引用的函式,並將庫同程式碼一起放到可執行檔案中。  
使用動態連結代替靜態連結有若干優點。DLL 節省記憶體,減少交換操作,節省磁碟空間,更易於升級,提供售後支援,提供擴充套件 MFC 庫類的機制,支援多語言程式,並使國際版本的建立輕鬆完成。  
lib與dll檔案最大區別在呼叫方面  
dll可以靜態陷入 
lib與DLL 
從這一章起,我講述的內容將特定於windows平臺。其實這篇文章也可看作是我在windows下的開發經驗總結,因為以後我決定轉unix了。 
前面有一章說編譯與連結的,說得很簡略,其實應該放到這一章一塊兒來說的。許多單講C++的書其實都過於學院派,對於真實的工作環境,上百個原始檔怎麼結合起來,幾乎沒有提及。我引導讀者一步步看看lib與DLL是怎麼回事。 
一個最簡單的C++程式,只需要一個原始檔,這個原始檔包含了如下語句 
int main(){return 0;} 
自然,這個程式什麼也不做。 
當需程式需要做事情時,我們會把越來越多的語句新增到原始檔中,例如,我們會開始在main函式中新增程式碼: 
#include <stdio.h> 
int main() 

printf("Hello World!\n"); 
return 0; 

由於人的智力水平的限制,當一個函式中包含了太多的語句時,便不太容易被理解,這時候開始需要子函式: 
#include <stdio.h> 
void ShowHello() 

printf("Hello World!\n"); 

int main() 

ShowHello(); 
return 0; 

同樣的道理,一個原始檔中包含了太多的函式,同樣不好理解,人們開始分多個原始檔了 
// main.cpp 
void ShowHello();//[1] 
int main() 

ShowHello(); 
return 0; 

// hello.cpp 
#include <stdio.h> 
void ShowHello() 

printf("Hello World!\n"); 

將這兩個檔案加入到一個VC工程中,它們會被分別編譯,最後連結在一起。在VC編譯器的輸出視窗,你可以看到如下資訊 
--------------------Configuration: hello - Win32 Debug-------------------- 
Compiling... 
main.cpp 
hello.cpp 
Linking...  
hello.exe - 0 error(s), 0 warning(s) 
這展示了它們的編譯連結過程。 
接下來,大家就算不知道也該猜到,當一個工程中有太多的原始檔時,它也不好理解,於是,人們想到了一種手段:將一部分原始檔預先編譯成庫檔案,也即lib檔案,當要使用其中的函式時,只需要連結lib檔案就可以了,而不用再理會最初的原始檔。 
在VC中新建一個static library型別的工程,加入hello.cpp檔案,然後編譯,就生成了lib檔案,假設檔名為hello.lib。 
別的工程要使用這個lib有兩種方式: 
1 在工程選項-〉link-〉Object/Library Module中加入hello.lib 
2 可以在原始碼中加入一行指令 
#pragma comment(lib, "hello.lib") 
注意這個不是C++語言的一部分,而是編譯器的預處理指令,用於通知編譯器需要連結hello.lib 
根據個人愛好任意使用一種方式既可。 
這種lib檔案的格式可以簡單的介紹一下,它實際上是任意個obj檔案的集合。obj檔案則是cpp檔案編譯生成的,在本例中,lib檔案只包含了一個obj檔案,如果有多個cpp檔案則會編譯生成多個obj檔案,從而生成的lib檔案中也包含了多個obj,注意,這裡僅僅是集合而已,不涉及到link,所以,在編譯這種靜態庫工程時,你根本不會遇到連結錯誤。即使有錯,錯誤也只會在使用這個lib的EXE或者DLL工程中暴露出來。 
關於靜態lib,就只有這麼多內容了,真的很簡單,現在我們介紹另外一種型別的lib,它不是obj檔案的集合,即裡面不含有實際的實現,它只是提供動態連結到DLL所需要的資訊。這種lib可以在編譯一個DLL工程時由編譯器生成。涉及到DLL,問題開始複雜起來,我不指望在本文中能把DLL的原理說清楚,這不是本文的目標,我介紹操作層面的東西。 
簡單的說,一個DLL工程和一個EXE工程的差別有兩點: 
1 EXE的入口函式是main或者WinMain,而DLL的入口函式是DllMain 
2 EXE的入口函式標誌著一段處理流程的開始,函式退出後,流程處理就結束了,而DLL的入口函式對系統來說,只是路過,載入DLL的時候路過一次,解除安裝DLL的時候又路過一次[2],你可以在DLL入口函式中做流程處理,但這通常不是DLL的目的,DLL的目的是要匯出函式供其它DLL或EXE使用。你可以把DLL和EXE的關係理解成前面的main.cpp和hello.cpp的關係,有類似,實現手段不同罷了。 
先看如何寫一個DLL以及如何匯出函式,讀者應該先嚐試用VC建立一個新的動態連結庫工程,建立時選項不選空工程就可以了,這樣你能得到一個示例,以便開始在這個例子基礎上工作。 
看看你建立的例子中的標頭檔案有類似這樣的語句: 
#ifdef DLL_EXPORTS 
#define DLL_API __declspec(dllexport) 
#else 
#define DLL_API __declspec(dllimport) 
#endif 
這就是函式的匯出與使用匯出函式的全部奧妙了。你的DLL工程已經在工程設定中定義了一個巨集DLL_EXPORTS,因此你的函式宣告只要前面加DLL_API就表示把它匯出,而DLL的使用者由於沒有定義這個巨集,所以它包含這個標頭檔案時把你的函式看作匯入的。通過模仿這個例子,你就可以寫一系列的標記為匯出的函數了。 
匯出函式還有另一種方法,是使用DEF檔案,DEF檔案的作用,在現在來說只是起到限定匯出函式名字的作用,這裡,我們要引出第二種[4]使用DLL的方法:稱為顯示載入,通過Windows API的LoadLibrary和GetProcAddress這兩個函式來實現[5],這裡GetProcAddress的引數需要一個字串形式的函式名稱,如果DLL工程中沒有使用DEF檔案,那麼很可能你要使用非常奇怪的函式名稱(形如:[email protected]@YAHXZ)才能正確呼叫,這是因為C++中的函式過載機制把函式名字重新編碼了,如果使用DEF檔案,你可以顯式指定沒編碼前的函式名。 

有了這些知識,你可以開始寫一些簡單的DLL的應用,但是我可以百分之百的肯定,你會遇到崩潰,而之前的非DLL的版本則沒有問題。假如你通過顯式載入來使用DLL,有可能會是呼叫約定不一致而引起崩潰,所謂呼叫約定就是函式宣告前面加上__stdcall __cdecl等等限定詞,注意一些巨集如WINAPI會定義成這些限定詞之一,不理解他們沒關係,但是記住一定要保持一致,即宣告和定義時一致,這在用隱式載入時不成問題,但是顯示載入由於沒有利用標頭檔案,就有可能產生不一致。 
呼叫約定並不是我真正要說的,雖然它是一種可能。我要說的是記憶體分配與釋放的問題。請看下面程式碼: 
void foo(string& str) 

str = "hello"; 

int main() 

string str; 
foo(str); 
printf("%s\n", str.c_str()); 
return 0; 

當函式foo和main在同一個工程中,或者foo在靜態庫中時,不會有問題,但是如果foo是一個DLL的匯出函式時,請不要這麼寫,它有可能會導致崩潰[6]。崩潰的原因在於“一個模組中分配的記憶體在另一個模組中釋放”,DLL與EXE分屬兩個模組,例子中foo裡面賦值操作導致了記憶體分配,而main中return語句之後,string物件析構引起記憶體釋放。 
我不想窮舉全部的這類情況,只請大家在設計DLL介面時考慮清楚記憶體的分配釋放問題,請遵循誰分配,誰釋放的原則來進行。 
如果不知道該怎麼設計,請抄襲我們常見的DLL介面--微軟的API的做法,如: 
CreateDC 
ReleaseDC 
的成對呼叫,一個函式分配了記憶體,另外一個函式用來釋放記憶體。 
回到我們有可能崩潰的例子中來,怎麼修改才能避免呢? 
這可以做為一個練習讓讀者來做,這個練習用的時間也許會比較長,如果你做好了,那麼你差不多就出師了。一時想不到也不用急,我至少見過兩個有五年以上經驗的程式設計師依然犯這樣的錯誤。

注[1]:為了說明的需要,我這裡使用直接宣告的方式,實際工程中是應該使用標頭檔案的。 
注[2]: 還有執行緒建立與銷燬也會路過DLL的入口,但是這對新手來說意義不大。 
注[3]:DEF檔案格式很簡單,關於DEF檔案的例子,可以通過新建一個ATL COM工程看到。 
注[4]:第一種方法和使用靜態庫差不多,包含標頭檔案,連結庫檔案,然後就像是使用普通函式一樣,稱為隱式載入。 
注[5]:具體呼叫方法請參閱MSDN。 
注[6]:之所以說有可能是因為,如果兩個工程的設定都是採用動態連線到執行庫,那麼分配釋放其實都在執行庫的DLL中進行,那麼這種情況便不會發生崩潰  

相關推薦

lib檔案 dll 檔案

什麼是lib檔案,lib和dll的關係如何   (1)lib是編譯時需要的,dll是執行時需要的。 如果要完成原始碼的編譯,有lib就夠了。 如果也使動態連線的程式執行起來,有dll就夠了。 在開發和除錯階段,當然最好都有。 (2)一般的動態庫程式有lib檔案和dll檔案。

Windows下的lib檔案dll檔案

一個軟體執行,通常需要一些必備的靜態庫和動態庫。靜態庫和動態庫本身,其實可以作為軟體執行所必需的環境來理解。靜態庫是指靜態的,一般在程式連結時早已放到程式中。動態庫是連結時告訴程式此環境存在,在執行時才呼叫和載入。在Windows下,靜態庫的檔名為lib,動態庫為dll。

靜態檔案模板檔案配置

STATIC_URL = '/static/' # 設定靜態檔案路徑,這裡改變的是使用者請求靜態檔案的網址路徑 #這是配置伺服器靜態檔案放置的位置 STATICFILES_DIRS = [ os.path.join(BASE_DIR, 'shop/static'),

php 解壓檔案壓縮檔案

配置環境變數 然後cmd 輸入 php -m 檢視是否有zip 選項 沒有的話zip功能將無法使用 function zip_file(string $filename) { if (!is_file($filename)) { return false;

php 上傳檔案下載檔案

上傳 /** * 單檔案上傳 * @param array $fileInfo 檔案資訊 是一個數組 * @param string $uploadPATH 檔案上傳預設路徑 * @param bool $imageFlag 是否檢測真實圖片 * @param arr

java讀取檔案寫入檔案

1. Java按行讀取Resource目錄下的檔案 List<String> historyList = new ArrayList<>(); ClassPathResource classPathResource = new ClassPathResource("tes

將字串寫入檔案讀取檔案

 一,讀取檔案內容 File file = new File(filePath); if(file.isFile() && file.exists()) {

java檔案,class檔案dex檔案的轉化。(詳細教程)

筆者這兩天整理的關於安卓逆向的一些小知識:教你如何在這三種檔案中來去自如: .java檔案 Java原始檔 .class檔案 Java位元組碼檔案,是一種能夠被Java虛擬機器(JVM:Java Virtual Machine)識別,載入並且執行的檔案格式。 .

文字檔案二進位制檔案區別

1)文字檔案:這類檔案以文字的ASCII碼形式儲存在計算機中。它是以"行"為基本結構的一種資訊組織和儲存方式。  2)二進位制檔案:這類檔案以文字的二進位制形式儲存在計算機中,使用者一般不能直接讀懂它們,只有通過相應的軟體才能將其顯示出來。二進位制檔案一般是可執行程式、圖形、

Java pcm檔案wav檔案互轉

Java pcm檔案與wav檔案互轉 2018年05月15日 14:32:28 -過期罐頭 閱讀數:1432 版權宣告:本文為博主原創文章,未經博主允許不得轉載。 https://blog.csdn.net/lys1220/article/details/80322562 程式碼所需要的

在ubuntuqtcreator中,如何將.a檔案.so檔案存放在工程目錄下並進行第三方庫的連結

目錄 背景: 執行環境: 3.連結庫 背景: 由於專案要在不同的計算機(Ubuntu系統)之間進行遷移,專案所依賴的第三方庫(包括動態連結庫以及靜態連結庫)在其他的計算機上並不一定存在,因此,打算將第三方庫放入專案的工程目錄下,在專案

fopen,文字檔案二進位制檔案

暮鼓集    行走集 在學習C語言檔案操作後,我們都會知道開啟檔案的函式是fopen,也知道它的第二個引數是標誌字串。其中,如果字串中出現’b’,則表明是以開啟二進位制(binary)檔案,否則是開啟文字檔案。 那麼甚麼是文字檔案,甚麼是二進位

mybatis全域性配置檔案對映檔案詳解

一、全域性配置檔案 1、概述 (1)SqlMapConfig.xml的配置內容和順序如下(順序不能亂): Properties(屬性) Settings(全域性引數設定) typeAliases(類型別名) typeHandlers(型別處理器)

將jar檔案dex檔案的轉換

一、dex檔案轉為jar檔案 1.第一篇 Android 反編譯資料整理 Made by 李文棟  [email protected] 2010-12-13  Monday 於北京 一、反編譯流程圖                   二、工具使用方法(命令

【Eclipse】根據檔名查詢檔案根據檔案中的字串查詢檔案

在大型專案開發中,你經常需要在Eclipse左方那龐大的檔案樹中尋找你需要的檔案。 此時,你直接使用Ctrl+Shift+R就可以喚出檔案查詢視窗。這是根據檔名來找檔案,如下圖,只要輸入你需要尋找的檔名,就能得到相應的尋找結果。 如果你需要根據檔案中的字串查詢檔案,那麼,

C++標頭檔案檔案結構

標頭檔案 在將一個程式分解成若干個檔案時,需要考慮識別符號在其他檔案中的可見性。使用標頭檔案是一個很有效的方法。如: # include<iostream.h> 其中的iostream.h是系統定義的一個檔案,這種以“.h”命名的檔案稱為“標頭檔案”。系統定義

QT筆記(6)——Qt開啟檔案儲存檔案

Qt開啟檔案與儲存檔案,首先開啟檔案,qt給了一個方法直接呼叫就好,如下: static QString getOpenFileName(QWidget *parent = Q_NULLPTR, con

Mybatis學習筆記(二)-Mybatis配置檔案對映檔案詳解

一、Mybatis配置檔案詳解 以下是mybatis.xml檔案,提倡放在src目錄下,檔名任意 <?xml version="1.0" encoding="UTF-8"?> <

C語言中標頭檔案檔案

1、什麼是庫檔案?     庫檔案中對常用的函式、方法進行實現並打包,使用者在使用時只需要呼叫庫檔案中的函式即可,不用再次對相關函式進行實現。 2、為什麼要有標頭檔案?     標頭檔案中是對庫檔案中的函式、方法實現進行宣告,標頭檔案可以避免重定義的風險,且標頭檔案裡包含了