VS2013下製作和使用靜態庫和動態庫

關於VS2013下製作和使用靜態庫和動態庫

引言

  • 什麼是庫:庫是寫好的現有的,成熟的,可以複用的代碼。
  • 所謂靜態、動態是指鏈接。將一個程序編譯成可執行程序的步驟:

靜態庫在鏈接階段,會將彙編生成的目標文件.o與引用到的庫一起鏈接打包到可執行文件中。因此對應的鏈接方式稱爲靜態鏈接。

爲什麼還需要動態庫?

  • 空間浪費是靜態庫的一個問題。
  • 另一個問題是靜態庫對程序的更新、部署帶來麻煩。如果靜態庫liba.lib更新了,所以使用它的應用程序都需要重新編譯、發佈給用戶(對於玩家來說,可能是一個很小的改動,卻導致整個程序重新下載,全量更新)。
  • 動態庫在程序編譯時並不會被連接到目標代碼中,而是在程序運行是才被載入。不同的應用程序如果調用相同的庫,那麼在內存裏只需要有一份該共享庫的實例,規避了空間浪費問題。動態庫在程序運行時纔會載入,也解決了靜態庫對程序的更新、部署帶來麻煩。用戶只需要更新動態庫即可,增量更新。

靜態庫

動態庫

VS2013下新建靜態庫

第一步:新建一個靜態庫(static library)項目

第二步:編寫項目內容

首先在頭文件下添加一個頭文件static.h
頭文件static.h的內容如下:

int sum(int a, int b);//聲明函數

在源文件下添加一個源文件static.cpp,內容如下:

#include "static.h"  
#include "stdafx.h"
int sum(int x, int y){
return x + y;}

第三步:生成.lib文件

在菜單欄選擇“生成”->“生成解決方案”就可以了。

然後打開工程文件夾,在Debug目錄下(編譯選擇的是默認的Debug和Win32)就可以看到一個和項目名稱相同的lib文件:

這樣供給別人調用的lib文件就生成好了,下面講如何調用這個靜態庫文件。

第四步:調用.lib文件

首先新建另外的項目TestCallLib1,這個項目將使用我們剛剛生成的靜態庫

將頭文件static.h和靜態庫TestLib1.lib拷到TestCallLib1\TestCallLib1目錄下

首先將主函數寫好

有兩種方法調用靜態庫:

1、右鍵“目錄”“屬性”選擇“鏈接器”->“輸入”,在“附加依賴項”這裏添加要調用的lib文件的名字:TestLib1.lib。

2、或者編寫代碼爲

運行得到結果!

動態庫的製作

創建動態庫關鍵是導出函數,DLL中導出函數的聲明有兩種方式:

  • 一種方式是:在函數聲明中加上__declspec(dllexport);
  • 另外一種方式是:採用模塊定義(.def)文件聲明,(.def)文件爲鏈接器提供了有關被鏈接程序的導出、屬性及其他方面的信息。

1、採用模塊定義(.def)文件聲明

新建項目win32,應用程序類型選擇dll

需要自己手動添加四個文件

頭文件dllgenerator.h,內容:

int Add(int, int);int Mul(int, int);

函數定義文件dllgenerator.cpp,內容:

int Add(int a, int b){return a + b;}
int Mul(int c, int d){return c * d;}

dllmain.cpp : 定義 DLL 應用程序的入口點

// dllmain.cpp : 定義 DLL 應用程序的入口點。

#include <windows.h>  
BOOL APIENTRY DllMain(HMODULE hModule,
DWORD  ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
    break;
}
return TRUE;
}

需包含庫

建立源文件Source.def(劃重點)

在此文件第一行引號中填你的項目名稱即可,EXPORTS下面列出要生成的函數名稱 格式:“名稱 @序號”)

LIBRARY "TestDll1" 
EXPORTS  
Add @1  
Mul @2  

點擊菜單欄 生成 -> 生成解決方案,我們的動態庫就生成成功了

2、通過關鍵字導出函數

新建項目過程同上!這裏的關鍵是頭文件中需要加入關鍵字__declspec(dllexport),這個關鍵字是導出函數的關鍵!

添加lib.h和lib.cpp函數

生成後同樣看見.dll文件和.lib文件

動態庫的加載——靜態加載

新建項目DLLTestor,將剛剛生成的.dll、.lib、.h文件拷貝到DLLTestor\DLLTestor文件下( .lib、.h 文件不是必須拷入的,我這裏並沒有拷入)

工程調用dll時首先在工程文件目錄中查找dll,找不到後在C:\Windows\System32 中找。所以我們自己項目簡單調用dll時就把生成的dll文件複製到工程目錄中,如果經常用可以把dll文件放到C:\Windows\System32中

調用動態庫是調用 .dll、 .lib、 .h 三個文件

編好代碼並設置.lib和.h文件的調用路徑

右鍵“目錄”“屬性”選擇“鏈接器”->“輸入”,在“附加依賴項”這裏添加要調用的lib文件的名字:TestDll1.lib

運行得到結果

動態加載和靜態加載

動態加載是指在生成可執行文件時不將所有程序用到的函數鏈接到一個文件,因爲有許多函數在操作系統帶的dll文件中,當程序運行時直接從操作系統中找。 

而靜態加載就是把所有用到的函數全部鏈接到exe文件中。動態加載是隻建立一個引用的接口,而真正的代碼和數據存放在另外的可執行模塊中,在運行時再裝入; 
 

而靜態加載是把所有的代碼和數據都複製到本模塊中,運行時就不再需要庫了。

動態加載

#include "stdafx.h"
#include "dllgenerator.h"
#include "windows.h"  
int _tmain(int argc, CHAR* argv[]){
printf("Hello World!\n");
HMODULE hmod = LoadLibrary("TestDll1.dll"); //  //用於加載dll
typedef int(*LoadProc)(int x, int y);
LoadProc Load_proc = (LoadProc)GetProcAddress(hmod, "Add");
//GetProcAddress()用於獲得函數地址
int iRet = Load_proc(3, 5); //得到地址後調用該函數,返回較大值
printf("the Add the value is:%x\n", iRet);
return 0;
}

需要理解調用動態庫主要用到三個函數,加載LoadLibrary、調用GetProcAddress、釋放FreeLibrary!

別忘了在右鍵“目錄”“屬性”選擇“鏈接器”->“輸入”,在“附加依賴項”這裏添加要調用的lib文件的名字:TestDll1.lib

大家在製作過程中可能會遇到如下的問題

這是字符集的問題,具體的解決方案是:右鍵項目->屬性

把字符集從unicode改成多字節字符集。

h頭文件 .lib庫文件 .dll動態鏈接庫文件關係

  • .h頭文件是編譯時必須的,lib是鏈接時需要的,dll是運行時需要的。
    附加依賴項添加的是.lib而不是.dll,若生成了DLL,則肯定也生成了LIB文件。
  • H文件的作用:聲明函數接口
  • DLL文件作用:函數可執行代碼
  • LIB文件作用:當我們在自己的程序中引用了一個H文件裏的函數,鏈接器怎麼知道該調用哪個DLL文件呢?這就是LIB文件的作用了。它告訴鏈接器調用的函數在哪個DLL中,函數執行代碼在DLL中的什麼位置,這也就是爲什麼需要附加依賴項.LIB文件,它起到橋樑的作用。
  • 如果是生成靜態庫文件,則沒有DLL,只有lib,這時函數可執行代碼部分也在lib文件中。

再說一點

目前以lib後綴的庫有兩種,一種爲靜態鏈接庫(Static Libary,以下簡稱“靜態庫”),另一種爲動態連接庫(DLL,以下簡稱“動態庫”)的導入庫(Import Libary,以下簡稱“導入庫”)。靜態庫是一個或者多個obj文件的打包,所以有人乾脆把從obj文件生成lib的過程稱爲Archive,即合併到一起。比如你鏈接一個靜態庫,如果其中有錯,它會準確的找到是哪個obj有錯,即靜態lib只是殼子。動態庫一般會有對應的導入庫,方便程序靜態載入動態鏈接庫,否則你可能就需要自己LoadLibary調入DLL文件,然後再手工GetProcAddress獲得對應函數了。有了導入庫,你只需要鏈接導入庫後按照頭文件函數接口的聲明調用函數就可以了。導入庫和靜態庫的區別很大,他們實質是不一樣的東西。靜態庫本身就包含了實際執行代碼、符號表等等,而對於導入庫而言,其實際的執行代碼位於動態庫中,導入庫只包含了地址符號表等,確保程序找到對應函數的一些基本地址信息。

一般的動態庫程序有lib文件和dll文件。lib文件是必須在編譯期就連接到應用程序中的,而dll文件是運行期纔會被調用的。如果有dll文件,那麼對應的lib文件一般是一些索引信息,具體的實現在dll文件中。如果只有lib文件,那麼這個lib文件是靜態編譯出來的,索引和實現都在其中。


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