vs导出和导入动态库和静态库

1. 动态库和导出和导入

1.1 动态库的导出

1. 创建新项目

新建新项目,选择动态链接库(DLL)。

在这里插入图片描述
填写项目名称,并选择项目保存的路径,然后点击创建。

在这里插入图片描述
创建完成后,会自动生成如下所示文件,可以根据需要自行修改文件名。其中,pch.h和pch.cpp一般是编写DLL函数的头文件和源文件。

在这里插入图片描述
同时,编译器还会帮你在属性管理器中做三件事:

  • 将配置类型设置为动态库

在这里插入图片描述

  • 在预处理中添加以你的工程名命名的动态库导出的宏定义,以我的工程名myDLL为例,会自动添加MYDLL.EXPORTS的宏定义,这个宏定义后面会用到。

在这里插入图片描述

  • 设置预编译头文件pch.h。这个就对应我们上面提到的pch.h和pch.cpp,如果我们不想使用vs给我们提供的pch.h和pch.cpp,可根据需要不使用预编译投或者修改预编译头文件的名字

在这里插入图片描述

2. 编写DLL函数

1.编写pch.h文件

// pch.h: 这是预编译标头文件。
// 下方列出的文件仅编译一次,提高了将来生成的生成性能。
// 这还将影响 IntelliSense 性能,包括代码完成和许多代码浏览功能。
// 但是,如果此处列出的文件中的任何一个在生成之间有更新,它们全部都将被重新编译。
// 请勿在此处添加要频繁更新的文件,这将使得性能优势无效。

#ifndef PCH_H
#define PCH_H

// 添加要在此处预编译的标头
#include "framework.h"

#ifdef MYDLL_EXPORTS
	#define DLLAPI __declspec(dllexport)
#else 
	#define DLLAPI __declspec(dllimport)
#endif

extern 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文件中,定义了全局变量、类以及函数。其中,MYDLL_EXPORTS 就是前面所述的宏定义,在定义了MYDLL_EXPORTS后,就会定义DLLAPI__declspec(dllexport)__declspec(dllexport)用于windows的动态库,其作用是声明导出变量、函数、类、对象等供外面调用,省略给出.def文件。

但是__declspec(dllexport)声明的函数会被转换为另一个名字,这是因为C语言中有函数的重载,而转换为另一个名字可以避免发生函数重载。当函数名被转换后,我们在导入这个DLL库时就无法引用这个函数了。然而,有一个方法可以避免这个事情的发生,这就是extern "C"的作用,它让编译器使用C方式的函数命名规则,这样,编译这个库后,函数名就不会发生转换。对于类,由于C语言中没有class,所以无需对class加上extern "C"

那有人有疑问了,说为什么还要有一个#define DLLAPI __declspec(dllimport)呢?其实,这个定义加不加对于导出库是没有任何影响的,但是对于导入库有影响。在MSDN文档里面进行了解释,意思是如果不定义#define DLLAPI __declspec(dllimport),就不能独自使用全局变量g_value,只能通过调用getValue()函数来返回g_value。也就是说,如果DLL库中没有定义全局变量,即使没有定义#define DLLAPI __declspec(dllimport),在导入该DLL库时编译也不会出现任何问题;但是一旦定义了全局变量,那导入该DLL库时,就会有两种情况,第一种情况是如果不独自使用该全局变量,编译也不会出现任何问题,通过调用getValue()函数也能返回正确的g_value,第二种情况是独自使用该全局变量,比如std::cout << g_value << std::endl;,那么在编译时就会报错,如下所示:

在这里插入图片描述

综上所述,一般在定义DLL的头文件时,需要加上#define DLLAPI __declspec(dllimport)这句。

2.编写pch.cpp文件

// pch.cpp: 与预编译标头对应的源文件

#include "pch.h"

// 当使用预编译的头时,需要使用此源文件,编译才能成功。
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. 生成动态库

点击 生成->生成解决方案 即可,注意这里解决平台是Debug x64,后面调用的时候也必须和这个平台一致,不然会报错。你也可以使用release,只要做到前后一致即可。

生成的myDLL.dllmyDLL.lib保存在${projectName}/x64/Debug目录下,如果你选择的其他release平台或者x86,就保存在相应的目录下。

在这里插入图片描述

很多小伙伴会比较疑惑的一点是,为什么我生成的DLL库,但却会伴随着lib文件呢?

其实,lib文件有两个意思,一个是静态库的意思,但在这里是是导入库的意思。二者的使用方式相同,含义完全不同。windows下的vs生成dll的时候会顺带生成lib(导入库),在导入DLL的时候可以显式导入,即指定DLL的名字和DLL里面函数的名字(这样比较麻烦);或者使用导入库辅助,这样就是为什么我们使用DLL的时候要在链接器指定lib(导入库)的原因了。

下面我们来看看如何导入动态库。

1.2 动态库的导入

1. 创建新项目

新建新项目,选择空项目。

在这里插入图片描述
填写项目名称,并选择项目保存的路径,然后点击创建。

在这里插入图片描述

2. 属性配置和添加DLL库

1.配置属性

  • 设置头文件目录

在这里插入图片描述

  • 设置库目录

在这里插入图片描述

  • 在链接器中添加导入库lib

在这里插入图片描述
2.添加DLL库到当前工作目录下

在这里插入图片描述
如果不添加DLL库,就会出现找不到DLL文件的报错。

说白了,上述的步骤是为了让项目可以找到库的头文件和库文件,最简单粗暴的方法是把.h(包含framework.h和pch.h)、.dll和.lib文件都复制到当前的工作目录下。这样,就无需进行前两项配置,即无需配置头文件目录的属性和库目录的属性了。

3. 编写调用代码

新建源文件,调用库的变量、函数和类。

#include "pch.h"
#include <iostream>

int main()
{
	//调用库函数
	int a = 1;
	int b = 2;
	int sum = myAdd(a, b);
	std::cout << sum << std::endl; //3
	//调用库变量
	std::cout << g_value << std::endl; //100
	//调用库类
	SimpleClass cls;
	int val = cls.getValue();
	std::cout << val << std::endl; //100
}

此时,需要注意的是,这里的导入DLL的项目中没有预定义MYDLL_EXPORTS,所以,pch.h中走的是#define DLLAPI __declspec(dllimport)这条支路,这里编译就可以顺利通过了,否则就会因为独自使用库中的全局变量而报错。

在这里插入图片描述

4. 生成可执行文件

点击三角符号进行生成并执行,在终端即可看到执行结果。

在这里插入图片描述
此时,在${projectName}/x64/Debug中即可看到exe文件。

在这里插入图片描述

2. 静态库和导出和导入

2.1 静态库的导出

1. 创建新项目

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

2. 编写LIB函数

1.编写pch.h文件

// pch.h: 这是预编译标头文件。
// 下方列出的文件仅编译一次,提高了将来生成的生成性能。
// 这还将影响 IntelliSense 性能,包括代码完成和许多代码浏览功能。
// 但是,如果此处列出的文件中的任何一个在生成之间有更新,它们全部都将被重新编译。
// 请勿在此处添加要频繁更新的文件,这将使得性能优势无效。

#ifndef PCH_H
#define PCH_H

// 添加要在此处预编译的标头
#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: 与预编译标头对应的源文件

#include "pch.h"

// 当使用预编译的头时,需要使用此源文件,编译才能成功。
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. 生成静态库

点击 生成->生成解决方案 即可,注意这里解决平台是Debug x64,后面调用的时候也必须和这个平台一致,不然会报错。你也可以使用release,只要做到前后一致即可。

生成的myDLL.lib保存在${projectName}/x64/Debug目录下,如果你选择的其他release平台或者x86,就保存在相应的目录下。

在这里插入图片描述

注意,这里的myLIB.lib的文件明显比导出动态库中的.lib文件要大,这也说明了.lib文件的两种含义。

2.2 静态库的导入

1. 创建新项目

在这里插入图片描述

在这里插入图片描述

2. 属性配置

  • 设置头文件目录

在这里插入图片描述

  • 设置库目录

在这里插入图片描述

  • 在链接器中添加导入库lib

在这里插入图片描述

说白了,上述的步骤是为了让项目可以找到库的头文件和库文件,最简单粗暴的方法是把.h(包含framework.h和pch.h)和.lib文件都复制到当前的工作目录下。这样,就无需进行前两项配置,即无需配置头文件目录的属性和库目录的属性了。

3. 编写调用代码

#include "pch.h"
#include <iostream>

int main()
{
	//调用库函数
	int a = 1;
	int b = 2;
	int sum = myAdd(a, b);
	std::cout << sum << std::endl; //3
	//调用库变量
	std::cout << g_value << std::endl; //100
	//调用库类
	SimpleClass cls;
	int val = cls.getValue();
	std::cout << val << std::endl; //100
}

4. 生成可执行文件

点击三角符号进行生成并执行,在终端即可看到执行结果。此时,在${projectName}/x64/Debug中即可看到exe文件。

3. 总结

DLL的导出步骤是:

  1. 创建DLL项目
  2. 编写DLL的.h文件和.cpp文件
  3. 生成DLL

DLL的导入步骤是:

  1. 创建空项目
  2. 配置属性和添加DLL库到工程目录
  3. 编写调用代码
  4. 生成可执行文件

LIB的导出步骤是:

  1. 创建LIB项目
  2. 编写LIB的.h文件和.cpp文件
  3. 生成LIB

LIB的导入步骤是:

  1. 创建空项目
  2. 配置属性
  3. 编写调用代码
  4. 生成可执行文件

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/62448.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

LabVIEW使用DSA技术从X射线图像测量肺气容量

LabVIEW使用DSA技术从X射线图像测量肺气容量 相衬X射线&#xff08;PCX&#xff09;成像技术利用相邻介质之间折射率的微小差异来增强传统X射线成像通常不可见的物体的边界。事实证明&#xff0c;这一进展在一系列生物医学和材料科学中非常有益于材料表征、疾病检测以及解剖形…

C语言 — 动态内存管理(动态内存函数)

前言 本期分为三篇介绍动态内存管理相关内容&#xff0c;关注博主了解更多 博主博客链接&#xff1a;https://blog.csdn.net/m0_74014525 本期介绍动态内存函数&#xff0c;函数如何使用、函数格式、在使用在所需要的注意点及C/C程序的内存开辟区域 系列文章 第一篇&#xff…

RGB_LCD简介

文章目录 前言一、LCD原理1、构造2、分辨率3、像素格式4、LCD接口5、ATK-7016 的屏幕接口原理图 二、LCD时序1、时间参数2、行场时序1、行显示时序&#xff1a;2、场显示时序&#xff1a;3、数据同步模式4、像素时钟 三、总结四、参考资料 前言 LCD (Liquid Crystal Display)是…

数据结构——双链表

我宁愿靠自己的力量&#xff0c;打开我的前途&#xff0c;而不愿求有力者垂青 文章目录 双线向链表各接口函数名或变量名 双向链表接口实现源码 快速索引【头文件及函数声明】 双向链表接口实现 双向链表的构造分析 双向链表的定义及初始化 双向链表的插入和删除 往期…

音频客观感知MOS对比,对ViSQOL、PESQ、MosNet(神经网络MOS分)和polqa一致性对比和可信度验证

原创&#xff1a;转载需附链接&#xff1a; https://blog.csdn.net/qq_37100442/article/details/132057139?spm1001.2014.3001.5502 一、背景 Mos分评价音质重要指标&#xff0c;最近也有很多机构和公司在研究适合自己的评价体系。目前Mos分主要分为主观评测和客观感知评价。…

智能仪表板DevExpress Dashboard v23.1亮点 - 增强对自定义导出的支持

DevExpress Dashboard v23.1版本增强了自定义导出到Excel的功能等&#xff0c;欢迎下载最新版本体验&#xff01; DevExpress Dashboard v23.1正式版下载(Q技术交流&#xff1a;523159565&#xff09; 所有平台 导出自定义仪表板项目到Excel 用户现在可以在WinForms和Web应…

危大工程智慧工地源码,微服务+Java+Spring Cloud +UniApp +MySql 物联网、人工智能、视频AI分析

一套智慧工地管理平台源码&#xff0c;PC端移动APP端可视货数据管理端源码 智慧工地可视化系统利用物联网、人工智能、云计算、大数据、移动互联网等新一代信息技术&#xff0c;通过工地中台、三维建模服务、视频AI分析服务等技术支撑&#xff0c;实现智慧工地高精度动态仿真&a…

C++ 多线程:std::future

std::future std::future 简介示例1博客引用来源 std::future 简介 我们前面介绍的std::thread 是C11中提供异步创建多线程的工具&#xff0c;只能是异步运行任务&#xff0c;却无法获取任务执行的结果&#xff0c;一般都是依靠全局对象&#xff0c;全局对象在多线程下是及其不…

4 三组例子,用OpenCV玩转图像-AI-python

读取&#xff0c;缩放&#xff0c;旋转&#xff0c;写入图像 首先导入包&#xff0c;为了显示导入matplotlib/为了在matplotlib显示 导入CV2/查看版本 导入图片/查看图片类型 图片数组 数组大小 对于opencv通道顺序蓝色B、绿色G、红色R matplotlib通道顺序为 红色R、绿色G、蓝…

快速修复应用程序中的问题的利器—— Android热修复

热修复技术在Android开发中扮演着重要的角色&#xff0c;它可以帮助开发者在不需要重新发布应用程序的情况下修复已经上线的应用程序中的bug或者添加新的功能。 一、热修复是什么&#xff1f; 热修复&#xff08;HotFix&#xff09;是一种在运行时修复应用程序中的问题的技术…

彻底弄清楚Minor GC和Major GC及Full GC

系列资源汇总 https://gitee.com/xiayi/java-docs 每日一句 每一日你所付出的代价都比前一日高&#xff0c;因为你的生命又消短了一天&#xff0c;所以每一日你都要更用心。 前提概要 对于JVM而言&#xff0c;最难能够掌握的就是GC回收部分的研究和探索。 而对于虚拟机而言根据…

前端页面--视觉差效果

代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><link rel"stylesheet" href"https://un…

如何实现基于场景的接口自动化测试用例?

自动化本身是为了提高工作效率&#xff0c;不论选择何种框架&#xff0c;何种开发语言&#xff0c;我们最终想实现的效果&#xff0c;就是让大家用最少的代码&#xff0c;最小的投入&#xff0c;完成自动化测试的工作。 基于这个想法&#xff0c;我们的接口自动化测试思路如下…

【Linux】【预】配置网络IP,挂载网络目录

【Linux】【预】配置网络IP&#xff0c;挂载网络目录 1. 配置查看IP2.配置Linux中的IP3. 串口连接开发板&#xff0c;配置 1. 配置查看IP a . 查看ipifconfig如下操作&#xff0c;其中的&#xff1a;192.168.252.140就是它的IP b . 使用xmodem 连接到虚拟机&#xff0c;最后点…

Docker搭建zookeeper

问题背景 前言 本文参考自&#xff1a;docker-compose快速搭建Zookeeper集群还有一种更加详细更加全面的部署方式&#xff1a;Docker之docker-compose一键部署Zookeeper集群&#xff0c;但笔者还未验证&#xff0c;先记录下来 搭建 安装docker-ce 此处不赘述 安装docker-co…

Docker快速入门笔记

Docker快速入门 前言 当今软件开发领域的一股热潮正在迅速兴起&#xff0c;它融合了便捷性、灵活性和可移植性&#xff0c;让开发者们欣喜若狂。它就是 Docker&#xff01;无论你是一个初学者&#xff0c;还是一位经验丰富的开发者&#xff0c;都不能错过这个引领技术浪潮的工…

MYSQL进阶-事务的基础知识

1.什么是数据库事务&#xff1f; 就是把好几个sql语句打包成一个整体执行&#xff0c;要么全部成功&#xff0c;要么全部失败&#xff01;&#xff01;&#xff01; 事务是一个不可分割的数据库操作序列&#xff0c;也是数据库并发控制的基本单位&#xff0c;其执 行的结果必…

03_012slab块分配器,管理内核内存分配,管理高速缓存

物理背景 为什么会有缓存cache 在最初开发ARM架构时&#xff0c;处理器的时钟速度和内存的访问速度大致相同。今天的处理器内核要复杂得多&#xff0c;其时钟速度可以快上几个数量级。但是&#xff0c;外部总线和内存设备的频率并没有扩大到同样的程度。有可能实现小块的片上S…

搭建SVN服务器

简介 SVN&#xff08;Subversion&#xff09;是一种版本控制工具&#xff0c;用于管理和跟踪文件的修改历史。它可以帮助团队协作开发&#xff0c;方便地共享和更新代码&#xff0c;同时也可以提供备份和安全控制功能。 使用SVN&#xff0c;你可以创建中央代码库&#xff08;…

【MySQL】对表中数据的操作

本期给大家带来的是MySQL下对表中数据的增删查改操作 目录 一、对表插入数据 1.1 单行数据插入 1.2 多行数据插入 1.3 插入冲突时更新数据 1.4 替换式插入 1.5 插入查询结果 二、对表中数据进行查询 2.1 基本select 2.1.1 使用select查询表中数据 2.1.2 使用select…