前言

C++的库文件分为两种:lib文件和dll文件,前者是静态的,会在build时就被打包到exe内,单独的一个exe文件就可以运行,而后者是动态的,不会被打包到exe内,除了exe,还需要对应的dll文件一起才可以运行。

C++的库文件分为两个部分,头文件和对应的cpp库文件,这也意味着我们在使用库文件时,需要用到这两个地方的路径。

举一个例子,创建一个空项目,创建Dependencies目录,里面存放网上下载的OpenGL的glfw内容,如下图所示:

include里存放了所有的头文件,lib-vc2017是用VS2017编译好的库文件的内容,里面有三个文件,其中glfw3.lib是预先编译好的静态库文件,glfw3.dll就是动态库文件,而glfw3dll.lib则是用来记录glfw3.dll里面的函数、symbol等内容的位置的,通过这个lib,Linker可以在dll中找到我们需要的函数,可用来加快程序运行效率,如果没有glfw3dll.lib文件程序也是可以执行的,只不过运行时计算机就会前往glfw3.dll里面去寻找我们需要的函数:

关于库文件在VS中的配置

项目属性中添加头文件
在项目属性的C+±>General里,如下图所示:

值得注意的是,在VC++ Directories下,也有一栏叫做Include Directories,这里设置的是整个VS2017的include文件目录(早期的VS把它放在了tools->options里面),比如说iostream这种的头文件,这里不要设置错了。


添加lib文件
在添加完头文件后,程序就可以编译了,但是运行确会报Link Error,对于静态库文件,需要在项目属性中设置lib文件的路径 ,既然是在Link阶段干的事情,自然是在Link界面里进行设置,如下图所示:

具体有两种方法:
第一种是简单的输入lib文件的路径,也就是在Input里面输入对应文件的路径,让Linker把这个文件Link进来,如下图所示:

点确定后可以成功Build项目,如下图所示:

可以看到这么长一串的路径在这里不太美观,因为其他的输入好像都是简单的lib文件名,如下图所示:

所以这里还有另外的方法,就是把lib文件的路径加入到Linker属性界面中,如下图所示,这样就可以在Input里面只输入glfw3.lib就可以了。


添加dll文件(通过lib定位)
前面演示了添加lib文件到项目中,这里演示添加dll文件,很多项目里dll和lib库的头文件是不一样的,但这里用到的glfw库的头文件是一样的,所以C++配置里头文件的目录不需要更改。

与前面类似,不过在Input里面不用glfw3.lib而是改用glfw3dll.lib,(glfw3dll.lib是在生成glfw3.dll时同时生成的lib文件,记载了dll里面各种符号和函数的位置)然后再把对应的glfw3.dll放到Debug下的HellowDLL的exe所在的目录下,因为exe默认默认调用DLL的路径就是exe所在的目录,这个路径也是可以改的,具体怎么改我还不太清楚。

也就是说,正常我们生成一个dll文件,其实还是需要对应的lib和dll文件,这样在使用该项目的属性中,需要在Input里面输入对应lib文件的路径和文件名,再把dll放到exe的同名目录下即可,当然,如果两个项目在同一个Solution下,我们可以直接使用项目引用(具体怎么操作见下一条)


添加dll文件(不通过lib定位)
具体使用一个裸的dll文件,需要用到显示链接,用C++的API去加载对应的DLL,文章最后面有提到这个事情


一个Solution下的一个项目生成lib以供另外一个项目使用
按照前面讲过的,设置项目A的生成属性为.lib文件,然后在项目B中添加A项目的头文件路径,再在项目B中的Linker界面输入项目A生成的Lib文件的目录和加入文件名,这样可以实现,但是Visual Studio提供了另外一种便捷的方法,就是项目引用,可以直接实现项目B使用项目A的lib或dll文件,这样我就不用辛苦的去手动设置路径了,比如说RecastDemo里面,所有实现的功能都是以lib格式生成的,最后演示的Demo就引用了所有这些项目,如下图所示:
组件都是用lib格式生成的

而演示Demo和测试项目引用了所有的lib组件,当Rebuild RecastDemo时,所有引用的项目都会被先Rebuild:

关于项目引用
VS执行项目引用的时候,实际上有一行对应的命令行操作,比如我这里有一个项目Hazel和项目Sandbox,Sandbox引用了Hazel,那么在Sandbox项目属性->linker->Command Line里,可以看到Link阶段Link了Hazel生成的dll对应的lib文件,我们就不需要手动再去Link下面添加对应的lib文件了,如下图所示:

C++语言生成dll的方法

前面总结的基本都是项目设置问题,具体C++怎么写代码生成dll接口呢?

正常对于一个想要做成dll的函数,在Windows平台可以在函数声明前面加上__declspec()关键字,如下所示:

namespace myDllNamespace
{// exportDLL.h __declspec(dllexport) void myFunction(); // 导出函数到dll中
}

注意这里把函数放到了自己定义的namespace里,不然别人导入dll后,myFunction就是一个全局函数,这很蠢,老式的C语言选择了复杂命名这样的方式,像glfw库那样,所有该dll下的函数都以glfw开头,叫做glfwInit()、glfwWindow()这种API,不过不推荐这么写

然后在导入该dll的工程中,我们也需要定义一个函数,表明这个函数是我们从dll导入的

// import.h
__declspec(dllimport) void myFunction(); // 导入dll中的函数

这也是为什么正常的dll导出头文件和dll导入的头文件是不一样的,相同的函数前面的前缀不同。

但通过宏,可以把两个头文件合并为一个,比如glfw的头文件却只用了一个头文件,如下所示,当使用不同的宏的时候,会随之导出dll、导入dll等操作:

/* GLFWAPI is used to declare public API functions for export* from the DLL / shared library / dynamic library.*/
#if defined(_WIN32) && defined(_GLFW_BUILD_DLL)     //Build DLL时导出/* We are building GLFW as a Win32 DLL */#define GLFWAPI __declspec(dllexport)
#elif defined(_WIN32) && defined(GLFW_DLL)                  //使用DLL时导入/* We are calling GLFW as a Win32 DLL */#define GLFWAPI __declspec(dllimport)
#elif defined(__GNUC__) && defined(_GLFW_BUILD_DLL)             //Linux平台使用dll/* We are building GLFW as a shared / dynamic library */#define GLFWAPI __attribute__((visibility("default")))
#else/* We are building or calling GLFW as a static library */#define GLFWAPI                                   //其他状态就啥也不用,因为static library已经嵌入到exe里了
#endif

理论上这样就可以了,但是我实际操作还是遇到了相关代码看不懂,这里举几个例子:

** extern “C”**
工作里我看到有这么定义导出dll头文件的

#ifdef _GNUC_
#include<jni.h>
#define EXPORT_API JNIEXPORT
#else
#define EXPORT_API __declspec(dllexport)
#endifextern "C"
{EXPORT_API bool  myFunction(){return true;} // 实际上定义是在cpp文件完成的
}

这里就涉及到了C语言与C++语言的区别,C语言不支持函数重载,C++语言支持函数重载,C++里通过Compiler用改变(mangle)函数名的方式来实现函数重载,通过在函数前面加上extern "C"限定符,能够规定Linker按照C语言的方式进行Link,能让C++编译器在编译该函数时不改变函数名。

举个例子,下列代码的func1没有问题,func2编译就会报错,因为C语言的Linkage方法里不支持函数重载:

什么时候使用extern “C”
当在C++中使用C语言的模块的时候,需要用extern "C"把对应的文件声明给括起来,表面这是用C语言写的,应该用C对应的Linkage的方式去Link

我们再回到之前的问题,为什么导出dll之前要加上extern"C"这一行代码:

extern "C"
{EXPORT_API bool  myFunction(){return true;}

有两种可能,第一,函数里面使用的c语言的函数,第二,为了方便查看函数名。
如果用C的Linkage规范,编译后的函数名更容易辨认,举个例子,有一个函数void WINAPI SampleFunc(void),C++规范编译后的名字是SampleFunc@@YGXXZ,而C编译后的名字是_SampleFunc@0,0代表没有参数(void),明显比后者直观很多。

隐式链接
隐式链接其实前面已经提到过了,通过项目属性里配置路径,可以完成隐式链接DLL,隐式链接需要三个内容:包含DLL函数声明的头文件、索引DLL相关函数位置的lib文件和代表DLL本身的.dll文件。

使用隐式链接的dll时,如果生成的项目与使用的项目在同一个Solution下,可以直接设置项目引用使用,如果是只有dll对应的.h、.lib和.dll文件,则需要在使用的工程中明确路径,既可以在项目属性中设置,也可以在脚本中表示引入了该dll,只不过这种情况下,需要把对应的lib文件和dll文件放在exe所在的目录下,如下所示:

//testDLL.h
#pragma comment(lib,"MyDll.lib") //会检索exe所在的目录
extern "C"_declspec(dllimport) int myFunc();  // 引入dll对应的函数//TestDll.cpp
#include
#include"Dlltest.h"
void main()
{// 调用dll
}

显示链接
显示链接支持在程序运行过程中动态加载和卸载dll,不过实现显式链接要麻烦一些。在应用程序中用LoadLibrary或MFC提供的AfxLoadLibrary显式的将自己所做的动态链接库调进来,动态链接库的文件名即是上述两个函数的参数,此后再用GetProcAddress()获取想要引入的函数。具体操作可以参考显示链接

附上讲C++的dll底层原理的视频链接,需要科学上网才能浏览:
https://www.youtube.com/watch?v=JPQWQfDhICA

C++中的库文件导入与导出相关推荐

  1. 添加库文件_PLC|教你把 Micro/WIN 的库文件导入到 Micro/WIN SMART 中

    教你把 Micro/WIN 的库文件导入到 Micro/WIN SMART 中 1. 打开S7-200 库文件 选择要导入的库文件,并将其放在程序编辑器的子程序中. 图1. 指令库 2. 配置相应参数 ...

  2. VC 6.0中添加库文件和头文件

    VC 6.0中添加库文件和头文件 分类: VC/VS 加头文件包含 VC6.0中: VC6.0默认include包含路径:Tools>Options>Directories>Incl ...

  3. java如何导入和导出ex_SpringBoot中关于Excel的导入和导出

    前言 由于在最近的项目中使用Excel导入和导出较为频繁,以此篇博客作为记录,方便日后查阅.本文前台页面将使用layui,来演示对Excel文件导入和导出的效果.本文代码已上传至我的gitHub,欢迎 ...

  4. php inputcsv,php实现CSV文件导入和导出

    这篇文章主要介绍了php实现CSV文件导入和导出的方法,具有一定的参考价值,需要的朋友可以参考下 项目开发中,很多时候要将外部CSV文件导入到数据库中或者将数据导出为CSV文件,那么具体该如何实现呢? ...

  5. power bi导入文件夹_从Power BI Desktop中的PDF文件导入数据

    power bi导入文件夹 Power BI Desktop is a useful reporting and analytical tool to represent data in variou ...

  6. Excel文件导入,导出

    前端Excel文件导入,导出 Excel文件导入,导出 功能快捷键 文件导入 文件解析 如何改变文本的样式 exportExcel.js 生成一个适合你的列表 创建一个表格 设定内容居中.居左.居右 ...

  7. java mvc 导入_Java SpringMVC文件导入和导出

    J示例代码: @ResponseBody @RequestMapping(value = "/fileUpload", method = RequestMethod.POST) p ...

  8. 计算机库网络不见了,在电脑中的库文件夹不见了的找回方法介绍

    ? 在微软操作系统中, Win7系统 以及以上版本的操作系统可以从任务栏中点击打开资源管理器来查找文件,而资源管理器默认打开的是库文件夹.如果在资源管理器的左侧列表中,发现找不到库文件夹的话,应该如何 ...

  9. linux读取dmp备份数据打开,Linux 中 Oracle dmp 文件导入导出(转)

    远程工具连接到 Linux 进行操作,进行 Linux 上 dmp 文件的导入导出. 1.将用户 system 用户表导出到指定路径 D 盘 exp system/password@SID file= ...

最新文章

  1. 如何将txt文档插入sql2000数据库
  2. 删除副本列表中的消失项目符号
  3. This dependency was not found: * !!vue-style-loader!css-loader?……解决方案
  4. Asp.net 类中使用中括号([......])的作用
  5. SpringBoot整合(Elasticserch)
  6. 怎么做装修预算?装修预算需要注意的三大事项
  7. 下载android平台源码
  8. maxvalue mysql自动分区_深入解析MySQL分区(Partition)功能
  9. appassembler-maven-plugin插件打包本地依赖的jar
  10. ”数独“android小游戏
  11. 普通计算机安装服务器系统安装教程,Windows Server操作系统详细安装教程
  12. 计算机专业英语教程(第二版)
  13. Hi3559a sdk环境搭建
  14. 华为云数据容灾方案助力中小企业发展
  15. 物联网的体系结构和关键技术
  16. 交互设计师修炼指南!教你从零开始成为优秀交互设计师
  17. 局域网内的计算机拒绝访问,win10系统局域网拒绝访问的解决方法
  18. h5登录input框浏览器输入账号密码样式设置
  19. 菜鸟的mysql高级进阶以及mysql数据库优化
  20. TwinCAT3第三方伺服电机——汇川IS620N使用

热门文章

  1. 锐捷交换机,路由器,无线,ESS,EG所有操作配置命令合集
  2. 电力电子技术总结-电力电子器件1
  3. 点击键盘的return键收回键盘
  4. 【人工智能哲学01/2】人工智能前世今生
  5. 终身伴侣(两个人的网站)代码+效果演示(文末源码地址)
  6. Piggy-Bank(最小完全背包问题)
  7. 全国2010年10月考试电子商务与电子政务试题
  8. 今天感受了一下ipad
  9. python 字符画
  10. 关于单片机对三极管B值测量的硬件电路和软件思路分享