下面我們將用兩種不同的姿勢來用VS2017生成dll文件(動態庫文件)和lib文件(靜態庫文件),這里以C語言為例,用最簡單的例子,來讓讀者了解如何生成dll文件(動態庫文件)
?
生成動態庫文件
姿勢一:
第一步:新建一個項目
?
第二步:選擇Windows桌面向導(這里先不要去管上面的“動態鏈接庫(DLL)”)
?
?第三步:選擇動態鏈接庫,并空項目打勾√
?
?第四步:添加一個.c源文件
?
第五步:(因為這里以C語言為例子,將后綴改為.c)
?
第六步:在c文件中輸入一個簡單的函數這里使用了_declspec(dllexport),但_declspec(dllexport)并不是必須的,后面一種方法將不使用_declspec(dllexport)
_declspec(dllexport) int sum(int a, int b)
{return a + b;
}
?
第七步:新建一個頭文件
?
?第八步:在頭文件中輸入函數的聲明
?
第九步:編譯
?
第十步:Debug文件夾下的兩個文件DLL.dll和DLL.lib就是我們要使用的兩個文件了
因為使用的_declspec(dllexport),雖然這里我們只編譯了一次,卻生成了dll和lib兩個文件
?
?
姿勢二:
?
第一步到第五步和上面的步驟一模一樣,這里從第六步開始講起
?
第六步:在c文件中輸入一個簡單的函數(注意這里就沒有使用_declspec(dllexport))
?
第七步:添加一個頭文件
?
第八步:在頭文件中輸入函數的聲明
?
第九步:編譯
?
第十步:這時在Debug文件里就可以看出兩種方法的區別了,第二種方法沒有加?_declspec(dllexport)?只有一個dll文件,如果我們也想要lib文件,需要額外幾個步驟
?
生成lib文件
?
第十一步:點擊項目——》DLL屬性
?
第十二步:配置屬性——》項目默認值——》配置類型,把動態庫(.dll) 改為 靜態庫(.lib)
?
第十三步:編譯
?
第十四步:這個時候Debug文件夾里面就多出了一個lib文件
從上面兩個例子可以看出:
在生成dll文件(動態庫文件)時,如果不使用_declspec(dllexport)那么就只有dll文件,在這種情況下就無法使用#pragma comment來隱式裝載動態庫(因為需要lib文件),只能使用LoadLibrary來顯式裝載動態庫(使用Loadlibrary只需要dll文件)
如果使用了_declspec(dllexport)那么就既有dll文件,也有lib文件
?
還有一點需要注意的是,如果在源文件(.c文件)中函數的定義沒有_declspec(dllexport),但是在頭文件中函數的聲明使用了_declspec(dllexport)
此時編譯產生的文件只有dll文件
,如果改成源文件中有_declspec(dllexport),頭文件中沒有_declspec(dllexport),那么編譯產生的文件既有dll文件也有lib文件
(導出dll文件時最好還是在源文件和頭文件中都加上_declspec(dllexport))
筆者記錄了一下加與不加_declspec(dllexport)對導出dll文件大小的影響,以上面的代碼為例
(造成dll文件大小不同的原因,筆者暫時無法給出解釋,待補充)
?
?
生成靜態庫文件
?
和生成dll文件步驟相似,這里就不再贅述了,直接上圖
第一步:
?
第二步:建議把預編譯標頭的勾去掉,(不去掉也沒事,只是本文為了簡潔,讓讀者更清楚的生成步驟)
?
第三步:
?
第四步:
?
第五步:
在源文件中輸入以下代碼:
int sum(int a, int b) {return a + b; }
?
第六步:
?
第七步:
?
第八步:
?
第九步:編譯,可以看到Debug文件夾下有一個lib文件
?
(注意:不要像我一樣傻fufu的,在導出lib文件的時候還加上_declspec(dllexport)(之前我的確這么干過),如果加了_declspec(dllexport),在Debug文件夾里面也只有lib文件,lib文件也能正常使用,
但是不建議加)
還有一點就是,生成dll文件(動態庫文件)時產生的lib文件,和生成lib文件(靜態庫文件)時產生的lib文件的作用不相同,從文件大小也能看出來(一個1.58KB一個3.92B)
關于lib和dll文件的區別可以看一下這一盤文章:lib 和 dll 的區別、生成以及使用詳解
?
?筆者記錄了一下加與不加_declspec(dllexport)對導出lib文件大小的影響,以上面的代碼為例
(至于為什么加了_declspec(dllexport)后,lib文件會出現0.02KB的差別,筆者暫時無法給出解釋,待補充)
?
有的讀者可能會發現在網上很多博客寫關于生成dll文件時,頭文件里面的寫法是這樣的
?
剛接觸預處理命令的讀者看著可能會有點不好理解,下面對上面的頭文件中的代碼逐個分析,筆者將上面的代碼分為兩個個部分(對預處理命令不是很熟悉的讀者可以先看一下這一篇隨筆:
(預處理命令使用詳解----#if、#endif、#undef、#ifdef、#else、#elif)
第一部分:
#pragma once #ifdef DLL_EXPORTS #define DLL _declspec(dllexport)#else #define DLL _declspec(dllimport)#endif
把上面的代碼翻譯一下就是:如果DLL_EXPORTS這個宏名已經被定義,那么DLL就等價于_declpsec(dllexport),否者DLL就等價于_declspec(dllimport),#pragma once保證了該頭文件只被包括(#include)一次,
在很多頭文件中都可以看到#pragma once,比如stdio.h
讀者這個時候可能就有疑問了,明明我沒有#define DLL_EXPORTS,為什么是執行#define DLL _declspec(dllexport)而不是#define DLL _declspec(dllimport)呢?
首先讀者需要知道的是DLL_EXPORTS是一個預定義的宏,因為我們是生成的是DLL文件
可以在屬性->配置屬性->C/C++->預處理器中看到
現在讀者應該清楚了,在生成DLL文件時,編譯器已經預定義了DLL_EXPORTS這個宏名,如果我們是生成的應用程序
上面的代碼寫成
#ifdef
```
#else
```
#endif
這種形式是為了方便在使用的時候lib或者dl文件時,需要引入頭文件的時候方便一點,不需要對頭文件做任何的修改(因為如果我們使用的配置類型是“應用程序(.exe)”,那么就沒有預定義DLL_EXPORTS)
?
第二部分:
#ifdef _cplusplus
extern "C"
{
#endifDLL int sum(int a, int b);
#ifdef _cplusplus
}
#endif
把上面的代碼翻譯一下就是:如果是C++文件(.cpp后綴)那么就是
extern "C"
{DLL int sum(int a, int b);
}
如果不是C++文件,那么就是
DLL int sum(int a, int b);
關于extern "C"作用,可以看一下這篇文章:深入理解C/C++混合編程(關于#ifdef __cplusplus extern "C" {...}的用法)
?
總結一下:
生成動態庫文件
?
頭文件:
1 #pragma once
2 #ifdef DLL_EXPORTS
3 #define DLL _declspec(dllexport)
4
5 #else
6 #define DLL _declspec(dllimport)
7
8 #endif
9
10 #ifdef _cplusplus
11 extern "C"
12 {
13 #endif
14 DLL int sum(int a, int b);
15 #ifdef _cplusplus
16 }
17 #endif
?
源文件:
1 _declspec(dllexport) int sum(int a, int b) 2 { 3 return a + b; 4 }
?
編譯之后產生:
?
生成靜態庫文件:
?
頭文件:
1 #pragma once
2
3 #ifdef _cplusplus
4 extern "C"
5 {
6 #endif
7 int sum(int a, int b);
8 #ifdef _cplusplus
9 }
10 #endif
?
源文件:
1 int sum(int a, int b)
2 {
3 return a + b;
4 }
?
編譯后產生:
?
到這里本文就基本結束了,上面詳細敘述了生成dll文件(動態庫文件)和lib文件(靜態庫文件)的步驟,關于lib文件和dll文件的使用將在另一篇隨筆中詳細介紹