用VS建立一个解决方案,包括两个项目:动态库TestDll和应用程序TestExe。以下详细介绍TestDll动态库配置导出类和函数的方法,以及TestExe应用程序如何调用TestDll和配置项目依赖关系。
一、VS2019创建动态库项目并导出类和函数
1、创建动态库项目TestDll
创建TestDll动态库成功后,VS已经为库项目声明了一个预处理定义符:TESTDLL_EXPORTS(自动生成规则:项目名称大写+下划线+EXPORTS):
添加global.h头文件,利用预处理定义TESTDLL_EXPORTS增加类导出符和函数导出符,如下所示:
#pragma once
//导出类:如果有预定义,就是导出的,否则就是导入的宏
#ifdef TESTDLL_EXPORTS
#define DLL_API __declspec(dllexport)
#else
#define DLL_API__declspec(dllimport)
#endif
//导出函数:如果有预定义,就是导出的,否则就是导入的宏
#ifdef TESTDLL_EXPORTS
#define DLL_API_C extern "C" __declspec(dllexport)
#else
#defineDLL_API_C extern "C" __declspec(dllimport)
#endif
//导出函数
DLL_API_C int calculate(int x, int y);
global.h文件,定义了DLL_API是导出类的声明符,DLL_API_C是导出函数的声明符,头文件最后还添加了一个caculate函数导出声明,caculate函数定义可在主函数的cpp文件中实现。
另外,TestDll项目添加了Shape父类,Circle和Square子类,这几个类的介绍可参见《C++虚函数多态和纯虚函数多态的经典用法》,这几个类以及整个解决方案可免费下载。TestDll项目组成如下所示:
2、导出Circle、Square类和calculate()函数
只有导出类和函数才能被其他程序调用。首先导出Circle类,修改Circle.h头文件
#pragma once
#include "global.h"
#include "Shape.h"
class DLL_API Circle:public Shape
{
public:
Circle(){};
Circle(std::string strName):Shape(strName) {};
~Circle(){};
void init() override;
void setRadius(int iRadius);
virtual float getArea() override;
virtual int getRadius() override;
private:
int _iRadius;
};
由上可见,Circle类增加DLL_API即可导出;
同理,Square类增加DLL_API导出。
然后在动态库主函数cpp文件中增加calculate()函数的实现:
#include "pch.h"
#include "global.h"
DLL_API_C int calculate(int x, int y) {
return x * y;
}
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;
}
由上可见,calculate()函数的实现也要由导出符DLL_API_C声明。
二、VS2019创建应用程序并调用动态库
1、创建TestExe应用程序
在解决方案中添加新控制台应用项目TestExe。解决方案中包含两个项目,一个是TestDll库项目,一个是TestExe应用程序项目,如下所示:
在TestExe.cpp应用程序文件中添加如下代码,包含Circle.h和Square.h头文件(global.h包含于两个类头文件,因此也包含了calculate()导出函数的声明),即可调用TestDll库的导出类Circle、Square和导出函数calculate():
#include <iostream>
#include "Circle.h"
#include "Square.h"
int main()
{
std::cout << "Call the dll function caculate. " << std::endl;
int iZ =caculate(2, 10);
std::cout << "caculate iZ:" << iZ << std::endl;
std::cout << "Please input a number :1 or 2. 1 means a circleshape,2 means a square shape. " << std::endl;
intiShapeType = 0;//1或者2分别代表圆和正方形
std::cin >>iShapeType;
Shape* pShape= nullptr;
if(iShapeType == 1)
pShape = new Circle("Circle");
else if (iShapeType == 2)
pShape = new Square("Square");
else {
std::cout << "Input error.";
return 0;
}
pShape->init();
float fArea =pShape->getArea();
std::cout <<pShape->getName() << " Area:" << fArea << std::endl;
int iPosX,iPosY;
pShape->getPos(iPosX, iPosY);
std::cout <<pShape->getName() << " CenterPosX:" << iPosX << " CenterPosY:" << iPosY << std::endl;
intiSideLen = pShape->getSideLength();
std::cout <<pShape->getName() << " SideLength:" << iSideLen << std::endl;
int iRadius= pShape->getRadius();
std::cout <<pShape->getName() << " Radius:" << iRadius << std::endl;
}
此时还需要配置TestExe,包含TestDll的所需头文件,以及显式配置TestDll库的导入。
2、配置TestExe调用TestDll库
(1)配置包含TestDll库的头文件路径
C/C++——>常规——>附加包含目录,填写相对路径:..\TestDll
(2)链接库设置,配置链接TestDll
TestDll库项目编译后生成TestDll.lib和TestDll.dll两个文件,采用 TestExe应用程序显式链接lib的配置方式,首先链接器——>常规——>附加库目录:配置TestDll的lib库编译生成路径。
其次添加TestExe应用程序的lib依赖,链接器——>输入——>附加依赖项:添加TestDll.lib。
至此TestExe应用程序项目配置调用动态库完毕,可以编译运行了(由于两个项目在一个解决方案中,TestDll生成的dll文件与TestExe的exe文件在同一个目录下,exe可直接运行)。另外,可设置TestExe项目依赖于TestDll项目(TestExe项目右键——>生成依赖项——>项目依赖项),每次TestExe重编译时,如果TestDll已经改动,会先编译TestDll后编译TestExe。
CSDN免费下载: C++动态库创建和应用程序调用动态库的源码