14.自己動手研究動態鏈接庫和靜態鏈接庫的區別

在學習注入dll時候 一直不理解如何區分動態鏈接庫和靜態鏈接庫
今天讓我們自己寫程序將它理解清楚
首先我們打開vs
創建一個動態鏈接庫文件
下面就是創建完文件之後 生成的代碼
我們在動態鏈接庫的入口處寫入自己的函數

自己動手寫一個dll導出函數

#include "pch.h"
// _declspec(dllexport) 聲明導出函數將該函數從該dll開放提供給其他函數使用
 extern "C" _declspec(dllexport) void ccc();
void ccc() {
    MessageBox(NULL,"導出函數被調用成功","信息:",MB_YESNO);
}
//api信息框 來查看是否調用成功
     //動態鏈接庫的入口函數
BOOL APIENTRY DllMain( HMODULE hModule,           //DLL模塊的句柄
                       DWORD  ul_reason_for_call, //DLL被調用的原因 
                       LPVOID lpReserved          //保留項也是Windows的保留參數
                     )                         //保留參數不是不使用的參數,而是Windows不想讓我們知道
                                             //作用的參數
{
    switch (ul_reason_for_call)
    { 
    case DLL_PROCESS_ATTACH:      //當進程加載時 DLL被調用
    case DLL_THREAD_ATTACH:         // 有線程被創建時DLL被調用
    case DLL_THREAD_DETACH:          //有線程結束時DLL被調用
    case DLL_PROCESS_DETACH:         //當DLL被進程卸載時DLL被調用
        break;
    }
    return TRUE;
}

extern "C"的主要作用就是爲了能夠正確實現C++代碼調用其他C語言代碼。加上extern "C"後,會指示編譯器這
部分代碼按C語言(而不是C++)的方式進行編譯。由於C++支持函數重載,因此編譯器編譯函數的過程中會將函數的
參數類型也加到編譯後的代碼中,而不僅僅是函數名;而C語言並不支持函數重載,因此編譯C語言代碼的函數時不
會帶上函數的參數類型,一般只包括函數名。
這個功能十分有用處,因爲在C++出現以前,很多代碼都是C語言寫的,而且很底層的庫也是C語言寫的,
爲了更好的支持原來的C代碼和已經寫好的C語言庫,需要在C++中儘可能的支持C,而extern "C"就是其中
的一個策略。

這個功能主要用在下面的情況:
C++代碼調用C語言代碼
在C++的頭文件中使用
在多個人協同開發時,可能有的人比較擅長C語言,而有的人擅長C++,這樣的情況下也會有用到

有人可能不理解爲什麼寫這麼多分支呢
答:因爲DLMain函數不止一次的被調用,每次調用可能執行不同的代碼,
比如進程加載dll時,可能在DLL中要申請一些資源,在進程卸載DLL時,DLL則要釋放自己申請的資源*/

程序與dll導出函數的靜態鏈接

我們創建完dll導出函數 再寫一個調用的程序
創建一個空項目c++

#include<Windows.h>
#pragma comment(lib,"Dll1")
extern "C" _declspec(dllexport) void ccc();
int main()
{
	ccc();
	return 0;
}

#pragma comment(lib,“dl”)注意:沒有分號
#pragma comnent(comment-type,["comentstring]”)
coment-type是一個預定義的標識籤,指定註釋的類型,應該是compiler,gxestr,1ib,linker之一。
coumentstrig.是一個提供爲comment-type提供附加信息的字符串。
#pragma comment( lib , “Dll1” )的意思是指本文件生成的.obj文件應與Dll1一起連接
寫完之後運行產生了不兼容的問題,我們需要更改一下屬性
在這裏插入圖片描述然後我們打開 dll導出函數和這個函數所在的文件位置

在這裏插入圖片描述
把dll導出函數的debug文件夾裏生成的這5個文件複製到
在這裏插入圖片描述由於是靜態鏈接,靜態鏈接把(lib)文件中用到的函數代碼直接鏈接進目標程序,程序運行的時候不再需要其它的庫文件
所以我們只用把lib後綴的文件複製到
在這裏插入圖片描述就可以運行了
我們總結一下
靜態鏈接庫的使用:
需要的文件: 頭文件 .h 、靜態庫 .lib
頭文件.h中有函數的聲明,使用靜態鏈接庫的項目需要引用該文件才能編譯通過
.lib包含了實際執行代碼、符號表等等
加載lib的方法: 法1.使用編譯鏈接參數或者VS的配置屬性來設置 法2.使用pragma編譯語句,例如pragma comment(lib,“a.lib”)
.lib中的指令將全部被直接包含在最終生成的 EXE 文件中

程序與dll導出函數的動態鏈接

創建一個空項目 -c++

 #include<Windows.h>
typedef void(*pMessage_Box)();
int main()
{
	HMODULE hModule = LoadLibrary("Dll1.dll");
	//判斷這個句柄是否爲空防止出bug
	if (hModule == NULL)
	{
		MessageBox(NULL, "句柄出錯","標題",MB_OK); 
			return -1;
	}
	//GetProcAddress是從指定的動態鏈接庫(DLL)中檢素導出的函數ccc的地址
	pMessage_Box plMsg = (pMessage_Box)GetProcAddress(hModule,"ccc");
		//執行函數
	plMsg();

	return 0;
}

用動態庫步驟:

1、創建一個函數指針,其指針數據類型要與調用的 DLL 引出函數相吻合。
2、通過 Win32 API 函數LoadLibrary()調用DLL,此函數返回DLL 的實例句柄。
3、通過 Win32 API 函數GetProcAddress()獲取要調用的DLL 的函數地址,把結果賦給自定義函數的指針類型。
4、使用函數指針來調用 DLL 函數。
5.最後調用完成後,通過 Win32 API 函數FreeLibrary()釋放DLL 函數。

我們再看一下他的文件
在這裏插入圖片描述經過了大量的嘗試 發現 只需要把dll放到這個位置 和程序同一個文件夾就可以了

靜態鏈接庫和動態鏈接庫的優缺點

從表面上來看

靜態鏈接

需要複製很多文件到可執行文件的debug中,然後再複製lib到工程項目下

動態鏈接

只需要一個dll在可執行文件的debug中 ,

深入的來看

靜態鏈接

是在在編譯時直接將代碼加入程序當中,而工程項目文件夾就是編譯生成的.obj等其他文件,鏈接器從靜態鏈接庫LIB獲取所有被引用函數,並將庫同代碼一起放到可執行文件中。

它的優點

(1) 代碼裝載速度快,執行速度略比動態鏈接庫快;

(2) 只需保證在開發者的計算機中有正確的.LIB文件,在以二進制形式發佈程序時不需考慮在用戶的計算機上.LIB文件是否存在及版本問題,可避免DLL地獄等問題。

它的缺點

使用靜態鏈接生成的可執行文件體積較大,包含相同的公共代碼,造成浪費;

動態鏈接

就是把調用的函數所在文件模塊(DLL)和調用函數在文件中的位置等信息鏈接進目標程序,程序運行的時候再從DLL中尋找相應函數代碼,因此需要相應DLL文件的支持。
動態庫在鏈接階段沒有被複制到程序中,而是程序在運行時由系統動態加載到內存中供程序調用。使用動態庫的優點是系統只需載入一次動態庫,不同的程序可以得到內存中相同的動態庫的副本,因此節省了很多內存
動態lib相當於一個h文件,是對實現部分(.dll文件)的導出部分的聲明。編譯後只是將導出聲明部分編譯到宿主程序中,運行時候需要相應的dll文件支持

它的優點

(1) 更加節省內存並減少頁面交換;

(2) DLL文件與EXE文件獨立,只要輸出接口不變(即名稱、參數、返回值類型和調用約定不變),更換DLL文件不會對EXE文件造成任何影響,因而極大地提高了可維護性和可擴展性;

(3) 不同編程語言編寫的程序只要按照函數調用約定就可以調用同一個DLL函數;

(4)適用於大規模的軟件開發,使開發過程獨立、耦合度小,便於不同開發者和開發組織之間進行開發和測試。

它的缺點

使用動態鏈接庫的應用程序不是自完備的,它依賴的DLL模塊也要存在,如果使用載入時動態鏈接,程序啓動時發現DLL不存在,系統將終止程序並給出錯誤信息。而使用運行時動態鏈接,系統不會終止,但由於DLL中的導出函數不可用,程序會加載失敗;速度比靜態鏈接慢。當某個模塊更新後,如果新模塊與舊的模塊不兼容,那麼那些需要該模塊才能運行的軟件,統統撕掉。這在早期Windows中很常見。

貼一個小知識

在這裏插入圖片描述

有人可能要問爲什麼兩個debug文件夾

在解決方案目錄下的是最終文件(即可執行文件)存放的目錄,在工程目錄下的是中間文件(編譯生成的.obj等其他文件)存放的目錄,中間文件對用戶來說沒什麼用的,只是在鏈接成最終文件時有用到

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章