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)
#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文件中,定义了全局变量、类以及函数。其中,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.dll
和myDLL.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的导出步骤是:
- 创建DLL项目
- 编写DLL的.h文件和.cpp文件
- 生成DLL
DLL的导入步骤是:
- 创建空项目
- 配置属性和添加DLL库到工程目录
- 编写调用代码
- 生成可执行文件
LIB的导出步骤是:
- 创建LIB项目
- 编写LIB的.h文件和.cpp文件
- 生成LIB
LIB的导入步骤是:
- 创建空项目
- 配置属性
- 编写调用代码
- 生成可执行文件
相关文章:

vs导出和导入动态库和静态库
1. 动态库和导出和导入 1.1 动态库的导出 1. 创建新项目 新建新项目,选择动态链接库(DLL)。 填写项目名称,并选择项目保存的路径,然后点击创建。 创建完成后,会自动生成如下所示文件,可以根据…...
30 使用easyExcel依赖生成Excel
30.1 导入依赖 <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>2.2.6</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId&…...

排序进行曲-v2.0
文章目录 小程一言直接插入排序步骤举例复杂度分析应用场景实际举例代码实现 希尔排序步骤举例复杂度分析应用场景实际举例代码实现 堆排序步骤举例复杂度分析应用场景实际举例代码实现 小程一言 这篇文章是在排序进行曲1.0之后的续讲, 由于在上一篇讲的排序的基本…...

反弹shell的N种姿势
预备知识1. 关于反弹shell 就是控制端监听在某TCP/UDP端口,被控端发起请求到该端口,并将其命令行的输入输出转到控制端。reverse shell与telnet,ssh等标准shell对应,本质上是网络概念的客户端与服务端的角色反转。2. 反弹shel…...

创意视频剪辑教程:快速合并视频并标题,让你的作品更吸睛!
想要让你的视频作品脱颖而出,引人注目?不再担心,我们为你带来了一款创意视频剪辑教程,教你如何快速合并视频并添加令人惊艳的标题效果!让你的作品在分钟内变得酷炫而精彩,向世界展示你的创意! …...

解决Hadoop审计日志hdfs-audit.log过大的问题
【背景】 新搭建的Hadoop环境没怎么用,就一个环境天天空跑,结果今天运维告诉我说有一台服务器磁盘超过80%了,真是太奇怪了,平台上就跑了几个spark测试程序,哪来的数据呢? 【问题调查】 既然是磁盘写满了&…...
【Java】java和kotlin关于Json写文件
Java写json文件 public class WriterJson {public static void main(String[] args) {// 创建一个 JSON 对象JSONObject jsonObject new JSONObject();jsonObject.put("case", "testtest");JSONObject jsonObjects new JSONObject();jsonObjects.put(&q…...

【深度学习】采用自动编码器生成新图像
一、说明 你知道什么会很酷吗?如果我们不需要所有这些标记的数据来训练 我们的模型。我的意思是标记和分类数据需要太多的工作。 不幸的是,大多数现有模型从支持向量机到卷积神经网,没有它们,卷积神经网络就无法训练。无监督学习不…...

华为云交付
文章目录 一、华为云-公有云架构华为公有云的主要服务1.华为云服务—计算类2.华为云服务——存储类3.华为云服务—网络类4.华为云服务—管理和监督类5.华为云数据库 二、待续 一、华为云-公有云架构 华为公有云的主要服务 ECS:弹性云服务器( Elastic Cl…...

dns瞅一瞅
正向解析—域名到ip 反向解析–ip到域名 域名本身是从又往左来解释的 根域—最顶层的域,用null字符标识,通常会省略最后的点和null字符,但是应用程序会在解析dns之前添加这些字符 顶级域— 两种类型,一种国家、地区代码的顶级域…...

springAOP的实例
文章目录 前言一.用户登录权限校验1.1 spring 拦截器1.2 传统的用户登录权限验证1.3 使用拦截器的方式1.4 案例1.5 拦截器实现原理 三.统一异常处理3.1 什么是统一异常处理3.2 具体步骤 四.统⼀数据返回格式4.1 为什么需要统一的数据返回4.2 统一返回数据的格式4.3 统一移除处理…...

【JavaEE】深入了解Spring中Bean的可见范围(作用域)以及前世今生(生命周期)
【JavaEE】Spring的开发要点总结(4) 文章目录 【JavaEE】Spring的开发要点总结(4)1. Bean的作用域1.1 一个例子感受作用域的存在1.2 通过例子说明作用域的定义1.3 六种不同的作用域1.3.1 singleton单例模式(默认作用域…...
P1320 压缩技术(续集版)
题目描述 设某汉字由 N N N \times N NN 的 0 \texttt 0 0 和 1 \texttt 1 1 的点阵图案组成。 我们依照以下规则生成压缩码。连续一组数值:从汉字点阵图案的第一行第一个符号开始计算,按书写顺序从左到右,由上至下。第一个数表示连续有…...

k8s(七) 叩丁狼 service Ingress
负责东西流量(同层级/内部服务网络通信)的通信 service的定义 apiVersion: v1 kind: Service metadata:name: nginx-svclabels:app: nginx-svc spec:ports:- name: http # service 端口配置的名称protocol: TCP # 端口绑定的协议,支持 TCP、…...

Android Studio 关于BottomNavigationView 无法预览视图我的解决办法
一、前言:最近在尝试一步一步开发一个自己的软件,刚开始遇到的问题就是当我们引用 com.google.android.material.bottomnavigation.BottomNavigationView出现了无法预览视图的现象,我也在网上查了很多中解决方法,最后在执行了如下…...

【STM32】小电流FOC驱控一体板(开源)
FOC驱控一体板 主控芯片stm32f103c8t6 驱动芯片drv8313 三相电流采样 根据B站一个UP主的改的(【【自制】年轻人的第一块FOC驱动器】),大多数元器件是0805,实验室具备且便于自己动手焊接 。 晶振用的是无源晶振,体…...

代码分析:循环创建N个子进程——为什么最后一个属于父进程?
黑马C/C 2018年32期代码分析 //循环创建n个子进程 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <unistd.h>int main() {int i 0;for(i0; i<3; i){//创建子进程pid_t pid fork();if(pid&…...
【SpringBoot面试题整理-超级有效】
文章目录 1.SpringBoot如何解决跨域问题?2.为什么要用Spring Boot?3. Spring Boot的约定优于配置,你的理解是什么?4. SpringBoot有哪些优点?5. Spring Boot中自动装配机制的原理?6.SpringBoot支持哪些日志框…...

岩土工程仪器多通道振弦传感器信号转换器应用于隧道安全监测
岩土工程仪器多通道振弦传感器信号转换器应用于隧道安全监测 多通道振弦传感器信号转换器VTI104_DIN 是轨道安装式振弦传感器信号转换器,可将振弦、温度传感器信号转换为 RS485 数字信号和模拟信号输出,方便的接入已有监测系统。 传感器状态 专用指示灯方…...

西瓜书读书笔记整理(五)—— 第四章 决策树
第四章 决策树 4.1 基本流程4.1.1 什么是决策树算法4.1.2 决策树学习的目的4.1.3 决策树学习基本过程4.1.4 决策树学习基本算法4.1.5 递归结束的三种情况 4.2 划分选择4.2.1 信息增益(information gain)—— ID3 决策树学习算法属性划分准则4.2.2 信息增…...
在软件开发中正确使用MySQL日期时间类型的深度解析
在日常软件开发场景中,时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志,到供应链系统的物流节点时间戳,时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库,其日期时间类型的…...

日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻
在如今就业市场竞争日益激烈的背景下,越来越多的求职者将目光投向了日本及中日双语岗位。但是,一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧?面对生疏的日语交流环境,即便提前恶补了…...
内存分配函数malloc kmalloc vmalloc
内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...

C++_核心编程_多态案例二-制作饮品
#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...

CTF show Web 红包题第六弹
提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框,很难让人不联想到SQL注入,但提示都说了不是SQL注入,所以就不往这方面想了 先查看一下网页源码,发现一段JavaScript代码,有一个关键类ctfs…...

微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】
微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来,Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...

React第五十七节 Router中RouterProvider使用详解及注意事项
前言 在 React Router v6.4 中,RouterProvider 是一个核心组件,用于提供基于数据路由(data routers)的新型路由方案。 它替代了传统的 <BrowserRouter>,支持更强大的数据加载和操作功能(如 loader 和…...

最新SpringBoot+SpringCloud+Nacos微服务框架分享
文章目录 前言一、服务规划二、架构核心1.cloud的pom2.gateway的异常handler3.gateway的filter4、admin的pom5、admin的登录核心 三、code-helper分享总结 前言 最近有个活蛮赶的,根据Excel列的需求预估的工时直接打骨折,不要问我为什么,主要…...

如何在看板中有效管理突发紧急任务
在看板中有效管理突发紧急任务需要:设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP(Work-in-Progress)弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中,设立专门的紧急任务通道尤为重要,这能…...

2025盘古石杯决赛【手机取证】
前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来,实在找不到,希望有大佬教一下我。 还有就会议时间,我感觉不是图片时间,因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...