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

0. 动态库和静态库的区别

静态库和动态库的最大区别是,静态库链接的时候把库直接加载到程序中,而动态库链接的时候,它只是保留接口,将动态库与程序代码独立,这样就可以提高代码的可复用度和降低程序的耦合度。 静态库在程序编译时会被连接到目标代码中,程序运行时将不再需要该静态库。

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.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文件的报错。

注意:有一个很重要的点,上述配置属性的时候为什么没有配置dll相关的路径,而只是配置了lib相关的路径,那请问编译器是如何找到dll文件的呢?这里就涉及到编译器的搜索路径了,编译器首先会在当前工作目录下搜索DLL,如果搜索不到,就会到系统的PATH中去搜索,如果系统的PATH都没有DLL的话,那么就会出现DLL库找不到的错误,所以我们这里添加DLL到工程目录中,就能让编译器找到这个DLL。我们再回想一下,Opencv里面DLL和LIB是分开两个文件夹安装的,而我们在VS中配置Opencv时没有配置与DLL相关的路径,也没有把DLL添加到工作目录,那为什么还能找到DLL,没错,就是因为我们安装完Opencv后把Opencv的bin路径添加到了系统的PATH中,而bin中包含了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. 生成可执行文件

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[oeasy]python0081_[趣味拓展]ESC键进化历史_键盘演化过程_ANSI_控制序列_转义序列_CSI

光标位置 回忆上次内容 上次了解了 新的转义模式 \033 逃逸控制字符 escape 这个字符 让字符串 退出标准输出流进行控制信息的设置 可以设置 光标输出的位置 ASR33中的ALT MODE 是 今天的ESC键吗&#xff1f;&#xff1f;&#xff1f;&#xff1f;&#x1f914; 查询文档…

ospf减少LSA更新

实验及实验要求 一、思路 1.根据区域划分IP地址 2.使公网可通---写缺省 3.使R3成为MGRE中心站点&#xff0c;R5、R6、R7为分支站点 4.一个个去配置ospf区域和RIP区域&#xff0c;确保每个区域配置无误 5.区域0要更改OSPF在接口的工作类型为broadcast &#xff0c;并使R3为…

【零基础学Rust | 基础系列 | 数据结构】元组,数组,向量,字符串,结构体

文章标题 简介&#xff1a;一&#xff0c;元组&#xff1a;1&#xff0c;定义元组&#xff1a;2&#xff0c;访问元组元素&#xff1a;3&#xff0c;元组解构&#xff1a;4&#xff0c;元组在函数中的应用&#xff1a; 二&#xff0c;数组&#xff1a;1&#xff0c;数组的声明和…

人工智能与物理学(软体机器人能量角度)的结合思考

前言 好久没有更新我的CSDN博客了&#xff0c;细细数下来已经有了16个月。在本科时期我主要研究嵌入式&#xff0c;研究生阶段对人工智能感兴趣&#xff0c;看了一些这方面的论文和视频&#xff0c;因此用博客记录了一下&#xff0c;后来因为要搞自己的研究方向&#xff0c;就…

Docker 启动 Nacos 报错:No DataSource set

​ &#x1f468;&#x1f3fb;‍&#x1f4bb; 热爱摄影的程序员 &#x1f468;&#x1f3fb;‍&#x1f3a8; 喜欢编码的设计师 &#x1f9d5;&#x1f3fb; 擅长设计的剪辑师 &#x1f9d1;&#x1f3fb;‍&#x1f3eb; 一位高冷无情的编码爱好者 大家好&#xff0c;我是 …

一款开源免费、更符合现代用户需求的论坛系统:vanilla

对于个人建站来说&#xff0c;WordPress相信很多读者都知道了。但WordPress很多时候我们还是用来建立自主发布内容的站点为主&#xff0c;适用于个人博客、企业主站等。虽然有的主题可以把WordPress变为论坛&#xff0c;但效果并不是很好。 所以&#xff0c;今天给大家推荐一个…

基于图像形态学处理的目标几何形状检测算法matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022a 3.部分核心程序 .................................................... %二进制化图像 Images_bin imbinari…

大数据技术之Clickhouse---入门篇---SQL操作、副本

星光下的赶路人star的个人主页 积一勺以成江河&#xff0c;累微尘以崇峻极 文章目录 1、SQL操作1.1 Insert1.2 Update 和 Delete1.3 查询操作1.4 alter操作1.5 导出数据 2、副本2.1 副本写入流程2.2 配置步骤 1、SQL操作 基本上来说传统关系型数据库&#xff08;以 MySQL 为例…

TiDB 源码编译之 PD/TiDB Dashboard 篇

作者&#xff1a; ShawnYan 原文来源&#xff1a; https://tidb.net/blog/a16b1d46 TiDB TiDB 是 PingCAP 公司自主设计、研发的开源分布式关系型数据库&#xff0c;是一款同时支持在线事务处理与在线分析处理 (Hybrid Transactional and Analytical Processing, HTAP) 的融…

使用C#的窗体显示与隐藏动画效果方案 - 开源研究系列文章

今天继续研究C#的WinForm的显示动画效果。 上次我们实现了无边框窗体的显示动画效果(见博文&#xff1a;基于C#的无边框窗体动画效果的完美解决方案 - 开源研究系列文章 )&#xff0c;这次介绍的是未在任务栏托盘中窗体的显示隐藏动画效果的实现代码。 1、 项目目录&#xff1b…

unity TextMeshPro 富文本

<b>粗体标签</b> <i>斜体标签</i> <u>下划线标签</u> <s>删除线标签</s> <sup>上标标签</sup>前面后边上标签 5<sup>。</sup>C <sub>下标标签&#xff0c;如&#xff1a;</sub>H<sub&…

解决Win11右键菜单问题

✅作者简介&#xff1a;大家好&#xff0c;我是Cisyam&#xff0c;热爱Java后端开发者&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;Cisyam-Shark的博客 &#x1f49e;当前专栏&#xff1a; 程序日常 ✨特色专栏&…