動態連線庫

2022-07-03 15:18:10 字數 4408 閱讀 7767

**:

windows下動態鏈結庫的編寫

在vc中新建乙個空的win32動態鏈結庫工程(win32 domanic library),然後新增乙個c++ sourse file到工程,我這裡的檔名取dlltest.cpp。然後在檔案中新增如下內容:

_declspec(dllexport)int add(int a,int b)

_declspec(dllexport)int subtract(int a,int b)

接下來編譯鏈結,就會在debug目錄下生成乙個除錯版本的動態鏈結庫,該鏈結庫包含了add和subtract兩個可供外部呼叫的函式。我們注意到,在原始檔中多了乙個沒有見過的語句declspec(dllexport) ,這個語句的作用就是向編譯器指出我需要在生成的動態鏈結庫中匯出的函式,沒有匯出的函式是不能被其他程式呼叫的。要知道乙個動態鏈結庫匯出了什麼函式,可以在命令提示行用命令"dumpbin -exports dlltest.dll"來檢視(也可以用vc工具包中的depends使用程式來檢視)。以下是用dumpbin命令檢視dlltest.dll而生成的資訊:

dump of file dlltest.dll

file type: dll

section contains the following exports for dlltest.dll

0 characteristics

4420bea4 time date stamp wed mar 22 11:04:04 2006

0.00 version

1 ordinal base

2 number of functions

2 number of names

ordinal hint rva      name

1    0 0000100a ?add@@yahhh@z

2    1 00001005 ?subtract@@yahhh@z

summary

7000 .data

1000 .idata

3000 .rdata

2000 .reloc

2a000 .text

可以看到,我們編寫的動態鏈結庫匯出了兩個函式,分別名為?add@@yahhh@z?subtract@@yahhh@z,為什麼名字不是addsubtract呢?這是因為

c++為了支援函式的過載,會在編譯時將函式的引數型別資訊以及返回值型別資訊加入到函式名中,這樣**中名字一樣的過載函式,在經過編譯後就互相區分開了,呼叫時

函式名也經過同樣的處理,就能找到對應的函式了。編譯器對函式的重新命名規則是與呼叫方式相關的,在這裡採用的是c++的預設呼叫方式。以此對應的還有stdcall方式、

cdecl方式、fastcall方式和thiscall方式,不同呼叫方式的重新命名規則不一樣。

需要特別說一下的是stdcall方式和cdecl方式:

stdcall方式(標準呼叫方式)也即pascal呼叫方式,它的重新命名規則是函式名自動加前導的下劃線,後面緊跟乙個@符號,其後緊跟著引數所佔位元組數,之所以要跟引數位元組數,是因為stdcall採用被調函式平衡堆疊方式,用函式名最後的數字告訴編譯器需要為函式平衡的位元組數。例如,如果我們的dlltest.dll採用stdcall方式編譯的話,匯出的函式名將會是_add@8_subtract@8,而函式編譯後的彙編**最後一句一定是 ret8。

cdecl方式即c語言呼叫方式,它的重新命名規則僅僅是在函式名前加下劃線(奇怪的是我用vc6編譯的c語言函式,名字沒有任何改變),因為c語言採用的是呼叫函式平衡堆疊的方式,所以不需要在函式名中加入引數所佔的位元組數,這樣的堆疊平衡方式也使c語言可以編寫出引數不固定的函式;同時c語言不支援函式過載,因此不需要在函式名中加入引數型別資訊和返回值型別資訊。

動態鏈結庫已經生成了,接下來就是呼叫的工作了。呼叫動態鏈結庫有兩種方式:隱式呼叫和顯式呼叫,下面我們分別來看兩種呼叫方式的具體過程:

動態鏈結庫的隱式呼叫

#include

using namespace std;

(1)extern int add(int a,int b);   //第一種呼叫方式

(2)_declspec(dllimport) int add(int a,int b);//第二種呼叫方式

int main()

編譯,沒有錯誤,鏈結,有兩個錯誤:找不到外部引用符號。要怎樣才能讓我們的程式找到動態連線庫中的函式呢?這裡是關鍵的一步。到剛才的dlltest工程目錄下,從debug資料夾中拷貝生成的dlltest.dll檔案和dlltest.lib檔案到dllcaller工程目錄。然後依次在vc中選擇選單:project -->settings-->liink, 在object/library modules中加入一項檔名:dlltest.lib,這裡的dlltest.lib並不是靜態庫檔案,而是dlltest.dll的導入庫檔案,它包含了dlltest.dll動態鏈結庫匯出的函式資訊,只有在工程鏈結設定裡新增了該檔案,才能夠使呼叫了該動態鏈結庫的工程正確鏈結。完成以上步驟後,我們再編譯鏈結工程,這次沒有任何錯誤!程式可以順利呼叫動態連線庫檔案,正常執行了(為了能使程式找到並載入需要的動態鏈結庫,動態鏈結庫檔案必須與呼叫程式在同乙個目錄下,或在path環境變數指定的目錄下)。

這裡需要說明一點,工程中的原始檔在呼叫動態鏈結庫中的函式時,需要提前宣告,聲名有兩種方式,一種是傳統的extern方式,一種是_declspec(dllimport)方式,這兩種方式在**中我都給出了。其中,第二種方式能使編譯過程更快,所以推薦使用。

動態鏈結庫的顯式呼叫

比起隱式呼叫,顯示呼叫更加靈活,而且在編譯鏈結時不需要lib導入庫檔案,也不需要提前宣告函式。我們通過windows提供的api函式來動態載入動態連線庫並呼叫其中的函式,用完後可以馬上釋放記憶體中的動態鏈結庫,十分方便。下面就是顯示呼叫動態鏈結庫的**:

#include

#include

using namespace std;

int main()

以上**並不複雜,首先定義乙個例項控制代碼用來引用由windows api函式loadlibrary載入的動態鏈結庫,loadlibrary函式的引數是乙個字串指標,具體呼叫時我們需要填入需要載入的動態鏈結庫的位置及檔名,載入成功後返回乙個例項控制代碼在getprocaddress函式中,我們指定的函式名必須是編譯後經過重新命名的函式名

,而不是原始檔中定義的函式名

。這樣實際上給我們的呼叫帶來了相當大的麻煩,因為我們不可能去了解每乙個經過重新命名的匯出函式名。好在微軟已經給出了解決方法,那就是在編寫動態鏈結庫時同時編寫乙個以def為字尾的編譯命名參考檔案,如果動態鏈結庫工程中有該檔案,則編譯器會根據該檔案指定的函式名來匯出動態庫函式,關於def檔案的詳細使用方法請參考msdn,這裡就不一一贅述。找到需要的動態庫函式後,我們就可以按需要對它進行呼叫,之後呼叫freelibrary函式釋放動態庫。因為動態庫是多程序共享的,因此呼叫freelibrary函式並不意味著動態庫在記憶體中被釋放,每個動態庫都有乙個變數用來記錄它的共享引用計數,而freelibrary的功能只是將這個記數減一,只有當乙個動態庫的引用計數為0時,它才會被作業系統釋放。

隱式呼叫與顯式呼叫的對比

前面已經詳細介紹了動態鏈結庫的兩種呼叫方法,相比之下,隱式呼叫在程式設計時比較簡單,指定導入庫檔案後,不必考慮函式的重新命名,就可以直接呼叫動態庫函式。但由於隱式呼叫不能指定動態庫的載入時機,因此在乙個程式開始執行時,作業系統會將該程式需要的動態鏈結庫都加載入記憶體,勢必造成程式初始化的時間過長,影響使用者體驗。而顯式呼叫採用動態載入的方法,用到什麼載入什麼,用完即釋放,靈活性較高,可以使程式得到優化。具體運用中到底採用哪種方法,還要依實際情況而定。

動態連線庫(DLL)

把c 的類寫進dll裡,單獨的編譯。程式執行時可以快速裝載並連線到dll上,這樣叫做標準的模組軟體。大大提高 的重用型。dll是執行時的模組。win32 dll允許匯出全域性變數,就象匯出函式一樣。只要dll申請記憶體,就從客戶程式的程序的記憶體堆中進行記憶體分配。dll包含乙個匯出函式表,它包含了...

生成動態連線庫 靜態連線庫的 makefile

靜態連線庫 擴充套件名為 a 是.o檔案的簡單集合。在 linux unix下,使用 ar 命令生成靜態連線庫。動態連線庫 擴充套件名為.so 是將.o檔案集合,並增加了匯出表。匯出表是乙個函式名 函式索引 函式位址的陣列。因此,應用程式可以裝載 使用 ldopen函式 後,根據函式名,匯出函式的索...

建立DLL動態連線庫

建立dll動態連線庫 3 製作dll 動態連線庫,但要宣告 uses unit1 in unit1.pas exports createform name myform 4 呼叫窗體的程式按普通方法製作,但是 在implementation下首先宣告要呼叫的dll函式 const gdi32 myf...