萊陽(yáng)網(wǎng)站建設(shè)個(gè)人免費(fèi)網(wǎng)站申請(qǐng)注冊(cè)
1. 動(dòng)態(tài)庫(kù)和導(dǎo)出和導(dǎo)入
1.1 動(dòng)態(tài)庫(kù)的導(dǎo)出
1. 創(chuàng)建新項(xiàng)目
新建新項(xiàng)目,選擇動(dòng)態(tài)鏈接庫(kù)(DLL)。
填寫項(xiàng)目名稱,并選擇項(xiàng)目保存的路徑,然后點(diǎn)擊創(chuàng)建。
創(chuàng)建完成后,會(huì)自動(dòng)生成如下所示文件,可以根據(jù)需要自行修改文件名。其中,pch.h和pch.cpp一般是編寫DLL函數(shù)的頭文件和源文件。
同時(shí),編譯器還會(huì)幫你在屬性管理器中做三件事:
- 將配置類型設(shè)置為動(dòng)態(tài)庫(kù)
- 在預(yù)處理中添加以你的工程名命名的動(dòng)態(tài)庫(kù)導(dǎo)出的宏定義,以我的工程名myDLL為例,會(huì)自動(dòng)添加MYDLL.EXPORTS的宏定義,這個(gè)宏定義后面會(huì)用到。
- 設(shè)置預(yù)編譯頭文件pch.h。這個(gè)就對(duì)應(yīng)我們上面提到的pch.h和pch.cpp,如果我們不想使用vs給我們提供的pch.h和pch.cpp,可根據(jù)需要不使用預(yù)編譯投或者修改預(yù)編譯頭文件的名字。
2. 編寫DLL函數(shù)
1.編寫pch.h文件
// pch.h: 這是預(yù)編譯標(biāo)頭文件。
// 下方列出的文件僅編譯一次,提高了將來(lái)生成的生成性能。
// 這還將影響 IntelliSense 性能,包括代碼完成和許多代碼瀏覽功能。
// 但是,如果此處列出的文件中的任何一個(gè)在生成之間有更新,它們?nèi)慷紝⒈恢匦戮幾g。
// 請(qǐng)勿在此處添加要頻繁更新的文件,這將使得性能優(yōu)勢(shì)無(wú)效。#ifndef PCH_H
#define PCH_H// 添加要在此處預(yù)編譯的標(biāo)頭
#include "framework.h"#ifdef MYDLL_EXPORTS#define DLLAPI __declspec(dllexport)
#else #define DLLAPI __declspec(dllimport)
#endifextern int DLLAPI g_value;class DLLAPI SimpleClass {
public:SimpleClass();~SimpleClass();int getValue() const;
};extern "C"
{DLLAPI int myAdd(int a, int b);DLLAPI int myMinus(int a, int b);DLLAPI int myMultipy(int a, int b);DLLAPI double myDevide(int a, int b);
}#endif //PCH_H
pch.h文件中,定義了全局變量、類以及函數(shù)。其中,MYDLL_EXPORTS
就是前面所述的宏定義,在定義了MYDLL_EXPORTS
后,就會(huì)定義DLLAPI
為__declspec(dllexport)
。__declspec(dllexport)
用于windows的動(dòng)態(tài)庫(kù),其作用是聲明導(dǎo)出變量、函數(shù)、類、對(duì)象等供外面調(diào)用,省略給出.def文件。
但是__declspec(dllexport)
聲明的函數(shù)會(huì)被轉(zhuǎn)換為另一個(gè)名字,這是因?yàn)镃語(yǔ)言中有函數(shù)的重載,而轉(zhuǎn)換為另一個(gè)名字可以避免發(fā)生函數(shù)重載。當(dāng)函數(shù)名被轉(zhuǎn)換后,我們?cè)趯?dǎo)入這個(gè)DLL庫(kù)時(shí)就無(wú)法引用這個(gè)函數(shù)了。然而,有一個(gè)方法可以避免這個(gè)事情的發(fā)生,這就是extern "C"
的作用,它讓編譯器使用C方式的函數(shù)命名規(guī)則,這樣,編譯這個(gè)庫(kù)后,函數(shù)名就不會(huì)發(fā)生轉(zhuǎn)換。對(duì)于類,由于C語(yǔ)言中沒有class,所以無(wú)需對(duì)class加上extern "C"
。
那有人有疑問(wèn)了,說(shuō)為什么還要有一個(gè)#define DLLAPI __declspec(dllimport)
呢?其實(shí),這個(gè)定義加不加對(duì)于導(dǎo)出庫(kù)是沒有任何影響的,但是對(duì)于導(dǎo)入庫(kù)有影響。在MSDN文檔里面進(jìn)行了解釋,意思是如果不定義#define DLLAPI __declspec(dllimport)
,就不能獨(dú)自使用全局變量g_value,只能通過(guò)調(diào)用getValue()
函數(shù)來(lái)返回g_value
。也就是說(shuō),如果DLL庫(kù)中沒有定義全局變量,即使沒有定義#define DLLAPI __declspec(dllimport)
,在導(dǎo)入該DLL庫(kù)時(shí)編譯也不會(huì)出現(xiàn)任何問(wèn)題;但是一旦定義了全局變量,那導(dǎo)入該DLL庫(kù)時(shí),就會(huì)有兩種情況,第一種情況是如果不獨(dú)自使用該全局變量,編譯也不會(huì)出現(xiàn)任何問(wèn)題,通過(guò)調(diào)用getValue()函數(shù)
也能返回正確的g_value
,第二種情況是獨(dú)自使用該全局變量,比如std::cout << g_value << std::endl;
,那么在編譯時(shí)就會(huì)報(bào)錯(cuò),如下所示:
綜上所述,一般在定義DLL的頭文件時(shí),需要加上#define DLLAPI __declspec(dllimport)
這句。
2.編寫pch.cpp文件
// pch.cpp: 與預(yù)編譯標(biāo)頭對(duì)應(yīng)的源文件#include "pch.h"// 當(dāng)使用預(yù)編譯的頭時(shí),需要使用此源文件,編譯才能成功。
int g_value = 100;SimpleClass::SimpleClass()
{
}SimpleClass::~SimpleClass()
{
}int SimpleClass::getValue() const
{return g_value;
}int myAdd(int a, int b) {return a + b;
}int myMinus(int a, int b) {return a - b;
}int myMultipy(int a, int b) {return a * b;
}double myDevide(int a, int b) {double m = (double)a / b;return m;
}
3. 生成動(dòng)態(tài)庫(kù)
點(diǎn)擊 生成->生成解決方案 即可,注意這里解決平臺(tái)是Debug x64,后面調(diào)用的時(shí)候也必須和這個(gè)平臺(tái)一致,不然會(huì)報(bào)錯(cuò)。你也可以使用release,只要做到前后一致即可。
生成的myDLL.dll
和myDLL.lib
保存在${projectName}/x64/Debug
目錄下,如果你選擇的其他release平臺(tái)或者x86,就保存在相應(yīng)的目錄下。
很多小伙伴會(huì)比較疑惑的一點(diǎn)是,為什么我生成的DLL庫(kù),但卻會(huì)伴隨著lib文件呢?
其實(shí),lib文件有兩個(gè)意思,一個(gè)是靜態(tài)庫(kù)的意思,但在這里是是導(dǎo)入庫(kù)的意思。二者的使用方式相同,含義完全不同。windows下的vs生成dll的時(shí)候會(huì)順帶生成lib(導(dǎo)入庫(kù)),在導(dǎo)入DLL的時(shí)候可以顯式導(dǎo)入,即指定DLL的名字和DLL里面函數(shù)的名字(這樣比較麻煩);或者使用導(dǎo)入庫(kù)輔助,這樣就是為什么我們使用DLL的時(shí)候要在鏈接器指定lib(導(dǎo)入庫(kù))的原因了。
下面我們來(lái)看看如何導(dǎo)入動(dòng)態(tài)庫(kù)。
1.2 動(dòng)態(tài)庫(kù)的導(dǎo)入
1. 創(chuàng)建新項(xiàng)目
新建新項(xiàng)目,選擇空項(xiàng)目。
填寫項(xiàng)目名稱,并選擇項(xiàng)目保存的路徑,然后點(diǎn)擊創(chuàng)建。
2. 屬性配置和添加DLL庫(kù)
1.配置屬性
- 設(shè)置頭文件目錄
- 設(shè)置庫(kù)目錄
- 在鏈接器中添加導(dǎo)入庫(kù)lib
2.添加DLL庫(kù)到當(dāng)前工作目錄下
如果不添加DLL庫(kù),就會(huì)出現(xiàn)找不到DLL文件的報(bào)錯(cuò)。
說(shuō)白了,上述的步驟是為了讓項(xiàng)目可以找到庫(kù)的頭文件和庫(kù)文件,最簡(jiǎn)單粗暴的方法是把.h(包含framework.h和pch.h)、.dll和.lib
文件都復(fù)制到當(dāng)前的工作目錄下。這樣,就無(wú)需進(jìn)行前兩項(xiàng)配置,即無(wú)需配置頭文件目錄的屬性和庫(kù)目錄的屬性了。
3. 編寫調(diào)用代碼
新建源文件,調(diào)用庫(kù)的變量、函數(shù)和類。
#include "pch.h"
#include <iostream>int main()
{//調(diào)用庫(kù)函數(shù)int a = 1;int b = 2;int sum = myAdd(a, b);std::cout << sum << std::endl; //3//調(diào)用庫(kù)變量std::cout << g_value << std::endl; //100//調(diào)用庫(kù)類SimpleClass cls;int val = cls.getValue();std::cout << val << std::endl; //100
}
此時(shí),需要注意的是,這里的導(dǎo)入DLL的項(xiàng)目中沒有預(yù)定義MYDLL_EXPORTS
,所以,pch.h中走的是#define DLLAPI __declspec(dllimport)
這條支路,這里編譯就可以順利通過(guò)了,否則就會(huì)因?yàn)楠?dú)自使用庫(kù)中的全局變量而報(bào)錯(cuò)。
4. 生成可執(zhí)行文件
點(diǎn)擊三角符號(hào)進(jìn)行生成并執(zhí)行,在終端即可看到執(zhí)行結(jié)果。
此時(shí),在${projectName}/x64/Debug
中即可看到exe文件。
2. 靜態(tài)庫(kù)和導(dǎo)出和導(dǎo)入
2.1 靜態(tài)庫(kù)的導(dǎo)出
1. 創(chuàng)建新項(xiàng)目
2. 編寫LIB函數(shù)
1.編寫pch.h文件
// pch.h: 這是預(yù)編譯標(biāo)頭文件。
// 下方列出的文件僅編譯一次,提高了將來(lái)生成的生成性能。
// 這還將影響 IntelliSense 性能,包括代碼完成和許多代碼瀏覽功能。
// 但是,如果此處列出的文件中的任何一個(gè)在生成之間有更新,它們?nèi)慷紝⒈恢匦戮幾g。
// 請(qǐng)勿在此處添加要頻繁更新的文件,這將使得性能優(yōu)勢(shì)無(wú)效。#ifndef PCH_H
#define PCH_H// 添加要在此處預(yù)編譯的標(biāo)頭
#include "framework.h"extern int g_value;class SimpleClass {
public:SimpleClass();~SimpleClass();int getValue() const;
};int myAdd(int a, int b);
int myMinus(int a, int b);
int myMultipy(int a, int b);
double myDevide(int a, int b);#endif //PCH_H
2.編寫pch.cpp文件
// pch.cpp: 與預(yù)編譯標(biāo)頭對(duì)應(yīng)的源文件#include "pch.h"// 當(dāng)使用預(yù)編譯的頭時(shí),需要使用此源文件,編譯才能成功。
int g_value = 100;SimpleClass::SimpleClass()
{
}SimpleClass::~SimpleClass()
{
}int SimpleClass::getValue() const
{return g_value;
}int myAdd(int a, int b) {return a + b;
}int myMinus(int a, int b) {return a - b;
}int myMultipy(int a, int b) {return a * b;
}double myDevide(int a, int b) {double m = (double)a / b;return m;
}
3. 生成靜態(tài)庫(kù)
點(diǎn)擊 生成->生成解決方案 即可,注意這里解決平臺(tái)是Debug x64,后面調(diào)用的時(shí)候也必須和這個(gè)平臺(tái)一致,不然會(huì)報(bào)錯(cuò)。你也可以使用release,只要做到前后一致即可。
生成的myDLL.lib
保存在${projectName}/x64/Debug
目錄下,如果你選擇的其他release平臺(tái)或者x86,就保存在相應(yīng)的目錄下。
注意,這里的myLIB.lib的文件明顯比導(dǎo)出動(dòng)態(tài)庫(kù)中的.lib文件要大,這也說(shuō)明了.lib文件的兩種含義。
2.2 靜態(tài)庫(kù)的導(dǎo)入
1. 創(chuàng)建新項(xiàng)目
2. 屬性配置
- 設(shè)置頭文件目錄
- 設(shè)置庫(kù)目錄
- 在鏈接器中添加導(dǎo)入庫(kù)lib
說(shuō)白了,上述的步驟是為了讓項(xiàng)目可以找到庫(kù)的頭文件和庫(kù)文件,最簡(jiǎn)單粗暴的方法是把.h(包含framework.h和pch.h)和.lib
文件都復(fù)制到當(dāng)前的工作目錄下。這樣,就無(wú)需進(jìn)行前兩項(xiàng)配置,即無(wú)需配置頭文件目錄的屬性和庫(kù)目錄的屬性了。
3. 編寫調(diào)用代碼
#include "pch.h"
#include <iostream>int main()
{//調(diào)用庫(kù)函數(shù)int a = 1;int b = 2;int sum = myAdd(a, b);std::cout << sum << std::endl; //3//調(diào)用庫(kù)變量std::cout << g_value << std::endl; //100//調(diào)用庫(kù)類SimpleClass cls;int val = cls.getValue();std::cout << val << std::endl; //100
}
4. 生成可執(zhí)行文件
點(diǎn)擊三角符號(hào)進(jìn)行生成并執(zhí)行,在終端即可看到執(zhí)行結(jié)果。此時(shí),在${projectName}/x64/Debug
中即可看到exe文件。
3. 總結(jié)
DLL的導(dǎo)出步驟是:
- 創(chuàng)建DLL項(xiàng)目
- 編寫DLL的.h文件和.cpp文件
- 生成DLL
DLL的導(dǎo)入步驟是:
- 創(chuàng)建空項(xiàng)目
- 配置屬性和添加DLL庫(kù)到工程目錄
- 編寫調(diào)用代碼
- 生成可執(zhí)行文件
LIB的導(dǎo)出步驟是:
- 創(chuàng)建LIB項(xiàng)目
- 編寫LIB的.h文件和.cpp文件
- 生成LIB
LIB的導(dǎo)入步驟是:
- 創(chuàng)建空項(xiàng)目
- 配置屬性
- 編寫調(diào)用代碼
- 生成可執(zhí)行文件