靜態庫:
靜態庫:指在我們的應用中,有一些公共代碼是需要反復使用,就把這些代碼編譯為“庫”文件;在鏈接步驟中,連接器將從庫文件取得所需的代碼,復制到生成的可執行文件中的這種庫。
靜態庫生成的lib文件:多個obj文件的合集
使用靜態庫步驟,配置工程屬性鏈接靜態庫
1拷貝頭文件,包含頭文件到要使用的工程項目中
2把lib添加到工程,下面2種方式都可以
2.1 添加工程引用的lib文件名:工程---屬性---配置屬性---鏈接器---輸入---附加依賴項:加上lib文件名(這種方式得把lib文件拷貝到當前目錄下)
2.2 #pragma comment(lib, "..\\debug\\staticlib.lib")//和2.1是一個意思,只是用代碼方便些(這種方式lib文件在當前目錄就不需要寫相對路徑或絕對路徑,我這個例子寫的是相對路徑)
#include"makestatic.h"
#include<iostream>
#pragma comment(lib,"makestatic.lib")//這個和附加依賴項是一樣,指定庫目錄中的文件(這是用代碼添加)
using namespace std;
int main()
{
int ret = test(10, 20);
cout << ret << endl;
system("pause");
return 0;
}
靜態庫使用缺點
每次使用都要重新編譯
多個程序使用同一個靜態庫,每個程序中都會有一份靜態庫代碼的拷貝(運行之前(編譯期間)會加載所有的代碼),會造成磁盤的資源的浪費
動態庫
動態庫的優點:使用DLL文件的好處是程序不需要在運行之初加載所有代碼,當被多個程序調用時只在內存中生成和使用同一個實例,使用DLL文件還可以減小程序的體積
動態庫生成的lib文件:存放是是dll的名字和函數名,這樣好通過lib文件用LoadLibrary和GetProcAddress去獲取函數地址調用
動態庫生成的dll文件:存放可供使用的函數、變量、代碼、類
生成動態庫的兩種方式動態庫:
1用__declspec(dllexport)聲明為導出函數
使用動態庫的時候得要__declspec(dllimport)聲明導入函數
如果要復用頭文件一般如下聲明,這樣的話在使用的時候就可以生成一份.h文件就可以了
#ifdef DLL_EXPORTS
#define DLL_API __declspec(dllexport)
#else
#define DLL_API __declspec(dllimport)
#endif // DLL_EXPORT
例子(宏是自己定義的):
函數聲明導出:DLL_API int MySub(int nVal1, int nVal2);
注意類聲明為導出語法不一樣關鍵字要寫在類后面:class DLL_API CMyTest
如果動態庫需要兼容c/c++和重用一份頭文件可以這樣寫
#ifdef DLL_EXPORTS
#define DLL_API __declspec(dllexport)
#else
#define DLL_API __declspec(dllimport)
#endif // DLL_EXPORT
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
DLL_API int MyAdd(int nVal1, int nVal2);
DLL_API extern int g_nValTest;
#ifdef __cplusplus
}
#endif // __cplusplus
2利用def文件導出函數來創建動態庫不需要使用關鍵字(__declspec(dllexport))聲明,只需要在選中添加新文件-代碼--def文件
注意點:def生成的動態庫 隱式加載除了全局變量要使用關鍵字聲明其他的都可以不使用__declspec(dllimport)聲明
def文件中,按照指定格式填寫需要導出的函數信息
/*
def導出
def為了解決跨編譯器使用, 導出函數的名字為函數本身, 不進行名稱粉碎,這樣的話使用動態加載函數名稱只需要寫同一個就可以了,就不需要在不同的編譯器下寫不同的名稱粉碎的名稱
多個進程加載同一個dll, 如果dll導出全局變量, 某個進程對全局變量的修改不會影響另一個進程.這里使用了寫時拷貝技術 + 引用計數.
def文件導出格式
LIBRARY
EXPORTS
導出的函數或變量名
*/
LIBRARY
EXPORTS
MyAdd = MySuperAdd @5 //也可以不加序號@5 或者不復制 直接寫導出名也可以
g_nVal
??0CMyTest@@QAE@XZ //導出類時,需要填寫所有類的函數導出名
??1CMyTest@@QAE@XZ
?Test@CMyTest@@QAEXXZ
//特別說明:使用格式
entryname[=internalname] [@ + ordinal [NONAME]] [DATA] [PRIVATE]
導出函數名 實際函數名 指定導出序號 不導出名字 數據格式 只允許動態加載使用
動態庫的使用(兩種方式):
1隱式加載動態庫
把.dll和.lib加上.h文件復制出來,拷貝到你要鏈接的那個工程中,把頭文件加載到你的工程項目中,記得頭文件的聲明要改為__declspec(dllimport) 聲明,(最好是定義一個宏去替代導出和導入,這樣只需要用同一個頭文件就可以了
,之后在.c文件中就可以調用動態庫的函數了
最后一步就是配置動態庫了,點擊屬性——配置屬性——鏈接器——附加在項目——編輯——添加你生成的.lib文件名,把.lib文件放到.c文件目錄下(或者用#pragma comment(lib, "..\\debug\\staticlib.lib"))
如果沒有把.lib文件放入到.c目錄下,會報無法打開.lib文件錯誤
運行一下如果沒有成功就把生成的.dll文件放到自己的工程文件中,如果是直接雙擊exe程序的話那當前exe程序的目錄就是當前目錄
2顯示加載動態庫
通過LoadLibrary和GetProcAddress兩個api加載動態庫
代碼例子如下
#include <windows.h>
typedef int(*PFN_MYADD)(int, int);
int main()
{
//DLL模塊加載到內存中,dll映射到當前進程使用的地址空間
//返回值:句柄,即dll在內存中的首地址
HMODULE hMod = LoadLibrary(TEXT("..\\Debug\\Dll.dll")); //參數指定要載入的動態鏈接庫的名稱
//獲取已經加載的DLL的句柄,如果dll沒有加載到內存中獲取會失敗,這個可以獲取系統提供dll模塊句柄,
//因為系統的dll模塊會默認調用LoadLibrary加載進進程中,例如獲取kernel32.dll模塊句柄
HMODULE hMod1 = GetModuleHandle(TEXT("..\\Debug\\Dll.dll"));//此時獲取的句柄LoadLibrary
//是一樣的,此時用GetProcAddress填的第一個參數是hMod 和hMod1都可以
if (hMod0 == NULL)
{
printf("GetModuleHandle fail\n");
}
//獲取的是主模塊句柄(調用這個函數的進程句柄),如果這個函數放在dll中,獲取的也是加載dll的進程句
//柄
HMODULE hMod2 = GetModuleHandle(NULL);//此時參數為null,獲取的是主模塊句柄
/*
函數原型:
FARPROC GetProcAddress(
HMODULE hModule, // DLL模塊句柄
LPCSTR lpProcName // 函數名或者用序號
);
*/
PFN_MYADD pfnMyAdd = (PFN_MYADD)GetProcAddress(hMod, "?MyAdd@@YAHHH@Z");
int nNum = pfnMyAdd(1, 2);
FreeLibrary(hMod);
return 0;
}
調用已經加載的DLL模塊的未導出函數
未導出函數的偏移量獲取方式:在工程目錄下的.map文件中查找
.map文件生成:項目-屬性-連接器-調試-生成映射文件
HMODULE hMod = LoadLibrary(TEXT("Dll.dll"));
PFN_TEST pfnTest = (PFN_TEST)((int)hMod + 0x11700);
//可根據加載的DLL模塊句柄 + 函數在DLL中的偏移量來獲取
隱式加載動態庫
優點:實現起來比較簡單,只需要用#pragma comment包含一下動態庫生成的.lib符號表,就會在程序運行的時候自動會把dll所有的函數加載到內存中,在程序中隨時調用dll中的任意一個函數,不要把這個想成靜態庫了,當多個程序都隱式加載的時候,dll一樣還是只有一份實例
缺點:這樣就會有和靜態庫同樣的缺點,假如你的exe需要加載多個dll的話,程序在啟動時把這些dll都加載到內存中會加大程序的啟動時間,並且一般情況只會在不同的條件下才會訪問dll中的其中一個函數,這樣的話用隱式調用就會造成資源的浪費
而且隱式加載也是使用顯示加載動態庫的方式,利用LoadLibrary和GetProcAddress兩個api加載所有的dll函數
顯示加載動態庫
優點:自己需要那些dll函數就用LoadLibrary和GetProcAddress兩個api獲取相對應的函數,這樣既提高程序啟動效率又不會浪費資源,顯示加載具有更好的靈活性,能更加有效的使用內存,在編寫大型程序時往往使用顯示加載方式。
缺點:代碼寫起來比較繁瑣
.dll加載的目錄順序
1) 當前目錄
2) 系統目錄
3) 環境變量
————————————————
版權聲明:本文為CSDN博主「code_greenhand」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/qq_35426012/article/details/72444536