VS環境下動態庫和靜態庫的建立和使用

1 動態庫和靜態庫的定義

1.1 庫的定義

庫是寫好的現有的,成熟的,可以複用的代碼。所謂靜態、動態是指鏈接。
       靜態庫在鏈接階段,會將彙編生成的目標文件.o與引用到的庫一起鏈接打包到可執行文件中。因此對應的鏈接方式稱爲靜態鏈接。
       動態庫在程序編譯時並不會被鏈接到目標代碼中,而是在程序運行是才被載入。動態庫鏈接的程序運行速度比靜態庫鏈接的慢。

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

   

 

1.2 將一個程序編譯成可執行程序的步驟

 

1.3 文件之間的關係

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

2 動態庫的建立與使用

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

 2.1   建立方法1

    在函數聲明中加上  __declspec(dllexport)。
    Step1:新建一個動態庫項目。   Win32項目->DLL
    Step2:編寫項目內容:xxx.h  xxx.cpp;項目添加新建項 xxx.def文件。添加就好,不需要修改任何東西。添加->代碼->模塊定義文件 .def。同時需要在xxx.h中添加如下內容:

//具體的宏定義名稱需要到 屬性->C/C++->命令行 查看,否則隨便定義的話會有警告
//這裏面用到了兩個編譯指令 __declspec(dllexport) 和__declspec(dllimport)

#ifdef  SEQLIST_DLL_EXPORTS  
#define DLL_API __declspec(dllexport)
#else
#define DLL_API __declspec(dllimport)
#endif
    
// 注意:上述宏定義後,需要在導出的函數聲明前添加“DLL_API”,在需要導出的 結構體/類 的struct/class 和變量名中間添加“DLL_API”。

Step3:生成 xxx.lib 文件。直接點擊生成解決方案。生成的lib文件在項目目錄的Debug文件夾中。
       Step4:如何使用看下面介紹,有兩種使用方式。

2.2   建立方法2

    採用模塊定義(.def)文件聲明,(.def)文件爲鏈接器提供了有關被鏈接程序的導出、屬性及其他方面的信息。
    Step1:新建一個動態庫項目。   Win32項目->DLL
    Step2:編寫項目內容:xxx.h  xxx.cpp  dllmain.cpp;項目添加新建項 xxx.def文件。dll.main文件的內容: 

    #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;
	}

   xxx.def文件的內容:

LIBRARY "動態庫項目名稱" 
EXPORTS 
函數名1  @1
函數名2  @2

Step3:生成 xxx.lib 文件。直接點擊生成解決方案。生成的lib文件在項目目錄的Debug文件夾中。
       Step4:如何使用看下面介紹,有兩種使用方式。

2.3   動態庫的加載

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

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

2.3.1 靜態加載 (經常使用)

       Step1: 新建項目,將剛剛生成的xxx.dll、xxx.lib、xxx.h文件拷貝到項目文件下      ( 如果xxx.lib、xxx.h 文件不拷入,需要包含它們的路徑)工程調用dll時首先在工程文件目錄中查找dll,找不到後在C:\Windows\System32 中找。所以我們自己項目簡單調用dll時就把生成的dll文件複製到工程目錄中,如果經常用可以把dll文件放到C:\Windows\System32中。
       Step2: 右鍵“目錄”“屬性”選擇“鏈接器”->“輸入”,在“附加依賴項”這裏添加要調用的lib文件的名字:xxx.lib。 如果不添加,需要在程序中使用    #pragma comment(lib, "xxx.lib")   //編譯器指令
       Step3:運行得到實驗結果

2.3.2 動態加載(缺乏lib文件的時候可以用這種方式)

例如:

#include "stdafx.h"
#include "xxxdll.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);

    //GetProcAddress()用於獲得函數地址
    LoadProc Load_proc = (LoadProc)GetProcAddress(hmod, "函數名"); 


    int iRet = Load_proc(3, 5); //得到地址後調用該函數,返回較大值
    printf("the Add the value is:%x\n", iRet);
    return 0;
}

          需要理解調用動態庫主要用到三個函數,加載LoadLibrary、調用GetProcAddress、釋放FreeLibrary!別忘了在右鍵“目錄”“屬性”選擇“鏈接器”->“輸入”,在“附加依賴項”這裏添加要調用的lib文件的名字:xxx.lib

3   靜態庫的建立與使用

    第一步:新建一個靜態庫(static library)項目。   Win32項目->靜態庫
    第二步:編寫項目內容   xxx.h  xxx.cpp
    第三步:生成 xxx.lib 文件。直接點擊生成解決方案。生成的lib文件在項目目錄的Debug文件夾中。
    第四步:如何使用。

  1. 新建工程項目,將頭文件xxx.h和靜態庫xxx.lib拷到項目目錄下。也可以不同拷貝,但是需要在項目屬性->配置屬性->vc++目錄->包含目錄/庫目錄分別添加 xxx.h和xxx.lib文件的絕對路徑。
  2. 右鍵“目錄”“屬性”選擇“鏈接器”->“輸入”,在“附加依賴項”這裏添加要調用的lib文件的名字:TestLib1.lib。如果不添加,需要在程序中使用: #pragma comment(lib, "xxx.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文件是靜態編譯出來的,索引和實現都在其中。

 

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