1. 程式人生 > >windows 下C++動態庫的封裝以及調用

windows 下C++動態庫的封裝以及調用

文件鏈接 系列 語義分析 ++ 動態鏈接 中大 動態 turn 應用

1、一個程序從源文件編譯生成可執行文件的步驟:
預編譯 --> 編譯 --> 匯編 --> 鏈接
(1)預編譯,即預處理,主要處理在源代碼文件中以“#”開始的預編譯指令,如宏展開、處理條件編譯指令、處理#include指令等。
(2)編譯過程就是把預處理完的文件進行一系列詞法分析、語法分析、語義分析以及優化後生成相應的匯編代碼文件。
(3)匯編是將匯編代碼轉變成二進制文件。
(4)鏈接將二進制文件鏈接成一個可執行的命令,主要是把分散的數據和代碼收集並合成一個單一的可加載並可執行的的文件。鏈接可以發生在代碼靜態編譯、程序被加載時以及程序執行時。鏈接過程的主要工作是符號解析和重定位。
2、庫
庫是一組目標文件的包,就是一些最常用的代碼編譯成目標文件後打包存放。而最常見的庫就是運行時庫(Runtime Library),如C運行庫CRT.
庫一般分為兩種:靜態庫(.a 、.lib)動態庫(.so 、.dll )所謂靜態、動態是指鏈接過程。

3、靜態庫與動態庫 靜態庫: 函數和數據被編譯進一個二進制文件(通常擴展名為.LIB)。在使用靜態庫的情況下,在編譯鏈接可執行文件時,鏈接器從庫中復制這些函數和數據並把它們和應用程序的其它模塊組合起來創建最終的可執行文件(.EXE文件)。
靜態庫有兩個重大缺點: 1)空間浪費
2)靜態鏈接對程序的更新、部署和發布會帶來很多麻煩。一旦程序中有任何模塊更新,整個程序就要重新鏈接,發布給用戶。 動態庫: 把程序按照模塊拆分成各個相對獨立的部分,在程序運行時才將它們鏈接在一起形成一個完整的程序,而不是想靜態鏈接一樣把所有的程序模塊都鏈接成一個單獨的可執行文件。 在使用動態庫的時候,往往提供兩個文件:一個引入庫和一個DLL。 引入庫包含被DLL導出的函數和變量的符號名,DLL包含實際的函數和數據。在編譯鏈接可執行文件時,只需要鏈接引入庫,DLL中的函數代碼和數據並不復制到可執行文件中,在運行的時候,再去加載DLL,訪問DLL中導出的函數。
特點:
1)代碼共享,所有引用該動態庫的可執行目標文件共享一份相同的代碼與數據。
2)程序升級方便,應用程序不需要重新鏈接新版本的動態庫來升級,理論上只要簡單地將舊的目標文件覆蓋掉。
3)在運行時可以動態地選擇加載各種應用程序模塊
區別:
(1)lib是編譯時用到的,dll是運行時用到的。如果要完成源代碼的編譯,只需要lib;如果要使動態鏈接的程序運行起來,只需要dll。 (2)如果有dll文件,那麽lib一般是一些索引信息,記錄了dll中函數的入口和位置,dll中是函數的具體內容;如果只有lib文件,那麽這個lib文件是靜態編譯出來的,索引和實現都在其中。使用靜態編譯的lib文件,在運行程序時不需要再掛動態庫,缺點是導致應用程序比較大,而且失去了動態庫的靈活性,發布新版本時要發布新的應用程序才行。 (3)動態鏈接的情況下,有兩個文件:一個是LIB文件,一個是DLL文件。LIB包含被DLL導出的函數名稱和位置,DLL包含實際的函數和數據,應用程序使用LIB文件鏈接到DLL文件。在應用程序的可執行文件中,存放的不是被調用的函數代碼,而是DLL中相應函數代碼的地址,從而節省了內存資源。DLL和LIB文件必須隨應用程序一起發行,否則應用程序會產生錯誤。如果不想用lib文件或者沒有lib文件,可以用WIN32 API函數LoadLibrary、GetProcAddress裝載。 下面重點介紹Windows下動態鏈接庫DLL.
DLL即動態鏈接庫(Dynamic-Link Libaray)的縮寫,相當於Linux下的共享對象。 Windows系統中大量采用了DLL機制,甚至內核的結構很大程度依賴與DLL機制。 Windows下的DLL文件和EXE文件實際上是一個概念,都是PE格式的二進制文件。一般的動態庫程序有lib文件和dll文件,lib文件是編譯時期連接到應用程序中的,而dll文件是運行時才會被調用的。 為了更好的理解DLL,首先介紹一下導出和導入的概念。
(1)導出與導入
在ELF(Linux下動態庫的格式),共享庫中所有的全局函數和變量在默認情況下都可以被其他模塊使用,即ELF默認導出所有的全局符號。DLL不同,需要顯式地“告訴”編譯器需要導出某個符號,否則編譯器默認所有的符號都不導出。
程序使用DLL的過程其實是引用DLL中導出函數和符號的過程,即導入過程。 對於從其他DLL導入的符號,需要使用“__declspec(dllimport)”顯式聲明某個符號為導入符號。在ELF中,使用外部符號時,不需要額外聲明該符號是從其他共享對象導入的。
指定符號的導入導出一般有如下兩種方法:
1)MSVC編譯器提供了一系列C/C++的擴展來指定符號的導入導出,即__declspec屬性關鍵字。
__declspec(dllexport) 表示該符號是從本DLL導出的
__declspec(dllimport) 表示該符號是從別的DLL中導入的
2)使用“.def”文件來聲明導入到導出符號,詳細參考《程序員的自我修養--鏈接、裝載與庫》。 4、DLL創建 新建一個Win32項目,命名為myDll--->選擇DLL類型,完成後項目結構如下: 技術分享圖片

修改myDll.h

// 下列 ifdef 塊是創建使從 DLL 導出更簡單的
// 宏的標準方法。此 DLL 中的所有文件都是用命令行上定義的 MYDLL_EXPORTS
// 符號編譯的。在使用此 DLL 的
// 任何其他項目上不應定義此符號。這樣,源文件中包含此文件的任何其他項目都會將
// MYDLL_API 函數視為是從 DLL 導入的,而此 DLL 則將用此宏定義的
// 符號視為是被導出的。
#ifdef MYDLL_EXPORTS
#define MYDLL_API __declspec(dllexport)
#else
#define MYDLL_API __declspec(dllimport)
#endif
// 此類是從 myDll.dll 導出的 class __declspec(dllexport) CmyDll { public: CmyDll(void); // TODO: 在此添加您的方法。 }; extern __declspec(dllexport) int nmyDll; __declspec(dllexport) int fnmyDll(void); extern "C" __declspec(dllexport) double seekArea(int r, int h);

myDll.cpp

// myDll.cpp : 定義 DLL 應用程序的導出函數。
//

#include "stdafx.h"
#include "myDll.h"
#include "stdio.h"



// 這是導出變量的一個示例
__declspec(dllexport) int nmyDll = 0;

// 這是導出函數的一個示例。
__declspec(dllexport) int fnmyDll(void)
{
    return 42;
}

// 這是已導出類的構造函數。
// 有關類定義的信息,請參閱 myDll.h
CmyDll::CmyDll()
{
    return;
}


void show()
{ 
    printf("Call the library function.\n");    
    printf("***************************\n");
}
double area(int r)
{ 
    return 3.14*r*r;
} 
__declspec(dllexport) double seekArea(int r, int h)
{ 
    show();    
    double under = 3.14*r*r;    
    double v = under*h;    
    return v;
}

編譯就會生成對應的dll文件,同時也會生成對應的lib文件。

技術分享圖片

註意:a.DLL中導出函數的聲明有兩種方式:在函數聲明中加上__declspec(dllexport);采用模塊定義(.def)文件聲明。詳見:http://www.cnblogs.com/enterBeijingThreetimes/archive/2010/08/04/1792099.html   b.對於C文件創建dll時或者想使用C編譯器創建dll時,建議使用 extern “C” 標誌

5.DLL的隱式調用
隱式鏈接采用靜態加載的方式,比較簡單,需要.h、.lib、.dll三件套。新建“控制臺應用程序”或“空項目”。配置如下:
項目->屬性->配置屬性->VC++ 目錄-> 在“包含目錄”裏添加頭文件testdll.h所在的目錄
項目->屬性->配置屬性->VC++ 目錄-> 在“庫目錄”裏添加頭文件testdll.lib所在的目錄

項目->屬性->配置屬性->鏈接器->輸入-> 在“附加依賴項”裏添加“testdll.lib”(若有多個 lib 則以空格隔開)。//也可以在項目屬性中設置庫的鏈接,#pragma comment(lib, "DLLSample.lib")

項目結構:

技術分享圖片

CallMyDll.cpp

// CallmyDll.cpp : 定義控制臺應用程序的入口點。
//

#include "stdafx.h"
#include "stdlib.h"
#include "myDll.h"

#pragma comment(lib, "myDll.lib")

extern "C" _declspec(dllimport)  double seekArea(int r, int h);  

int _tmain(int argc, _TCHAR* argv[])
{ 
    int r = 1, h = 5;    
    double area = seekArea(r, h);    
    printf("Area is:%f\n", area);    
    system("pause");    
    return 0;
}

運行前:將動態庫文件myDll.dll拷貝到可執行文件目錄下,否則會報錯。運行結果:

技術分享圖片

原文: https://blog.csdn.net/zhangfuliang123/article/details/71515796

windows 下C++動態庫的封裝以及調用