一、Windows的动态链接库简介

DLL即动态链接库(Dynamic-Link Library)的缩写,相当于Linux下的共享对象。Windows系统中大量采用DLL机制,甚至内核的结构很大程度依赖于DLL机制。Windows下的DLL文件和EXE文件实际上是一个概念,都是PE格式的二进制文件。

1.1 Windows下面的动态链接库与Linux下面的动态链接库的区别

(1)文件后缀不同

Linux动态库的后缀是 .so 文件,而window则是 .dll 文件

(2)文件格式不同

(a)Linux下是ELF格式,即Executable and Linkable Format。在ELF之下,共享库中所有的全局函数和变量在默认情况下都可以被其它模块使用,即ELF默认导出所有的全局符号。

(b)Windows下面是PE格式的文件,即Portable Executable Format。DLL本质上也是PE文件,DLL需要显示地“告诉”编译器需要导出某个符号,否则编译器默认所有的符号都不导出。

(c)动态链接库的文件个数不一样

Linux的动态链接库就只有一个 .so 文件,还有与之对应的头文件,而在Windows下面的动态库有两个文件,一个是引入库(.LIB)文件,一个是动态库(.DLL)文件,需要的头文件(.h)文件。

(1)引入库文件包含被DLL导出的函数的名称和位置,对于导入库而言,其实际的执行代码位于动态库中,导入库只包含了地址符号表等,确保程序找到对应函数的一些基本地址信息。

(2)DLL文件包含实际的函数和数据,应用程序使用LIB文件链接到所需要使用的DLL文件,库中的函数和数据并不复制到可执行文件中,因此在应用程序的可执行文件中,存放的不是被调用的函数代码,而是DLL中所要调用的函数的内存地址,这样当一个或多个应用程序运行是再把程序代码和被调用的函数代码链接起来,从而节省了内存资源。

总结:从上面的说明可以看出,Windows下面所创建的动态链接库DLL和.LIB文件必须随应用程序一起发行,否则应用程序将会产生错误。一般的动态库程序有lib文件和dll文件,lib文件是编译时期连接到应用程序中的,而dll文件才是运行时才会被调用的。

1.2 静态链接库和动态链接库的异同点

如果采用静态链链接库(.lib),lib中的指令最终都会编译到链接该静态库的exe(或dll)文件中,发布软件时,只需要发布exe(或dll)文件,不需要.lib文件。

但是若使用动态链接库(. dll),dll中的指令不会编译到exe文件中,而是在exe文件执行期间,动态的加载和卸载独立的dll文件,需要和exe文件一起发布。

静态链接库不能再包含其他动态链接库或静态链接库,而动态链接库不受此限制,动态链接库中可以再包含其他的动态链接库和静态链接库。

来自:https://blog.csdn.net/w_y2010/article/details/80428067

1.3 动态链接库的优点

1)节省内存和代码重用:当应用程序使用动态链接时,多个应用程序可以共享磁盘上单个DLL副本

2)可扩展性:DLL文件与EXE文件独立,只要接口不变,升级程序只需更新DLL文件不需要重新编译应用程序

3)复用性:DLL的编制与具体的编程语言以及编译器无关,不同语言编写的程序只要按照函数调用约定就可以调用同一个DLL函数1

1.4 有下面的代码

  • 头文件framework.h
#pragma once
namespace mycal
{int add(int a, int b);int sub(int a, int b);
}
  • 实现代码 framework.cpp
//framework.cpp
#include "framework.h"int mycal::add(int x, int y)
{return x + y;
}int mycal::sub(int x, int y)
{return x - y;
}

在Linux下,编译成动态链接库之后,会得到一个 xxx.so 文件,现在只要引入头文件,包含动态库路径,就可以正常使用了,但是上面的代码同样在Windows下面,使用VS2017编译成动态链接库之后,的确不会报错,只会的到一个 xxx.dll 文件,(不是还有一个对应的 xxx.lib文件吗,哪里去了呢?)

然后我们新建一个项目,按照 “头文件路径配置——库文件路径配置”的方法,编写代码,我们也可以调用到add这两个函数,还有语法提示,因为语法提示其实来自于头文件,和库文件没关系,但是编译却不成功了,显示调用的add以及sub都是错误的,这是为什么呢?

这是因为前面说了Windows下面需要显式的告诉编译器,动态库中有哪一些函数是可以导出使用的,上面没有显示说明,即add和sub实际上是不可以使用的,故而会报错,怎么办呢?参见下面。

二、Windows平台之下使用VS2017如何创建动态库

2.1 解决未生成lib文件以及函数没有显式导出的问题

两种方式来决定动态库中到底哪些函数是可以导出供外部直接使用的,以及与此同时生成与dll对应的 .lib 文件。

(1)MSVC编译器提供了关键字_declspec,来指定指定符号的导入导出,即_declspec属性关键字

_declspec(dllexport) 表示该符号是从本DLL导出的符号:这是在定义DLL中的函数等源代码是必须使用的,如果不显式的导出某一些符号,则使用动态链接库虽然没有语法上的错误,但是她无法编译,因为dll中的函数没有暴露出来,故而找不到。

_declspec(dllimport)表示该符号是从别的DLL中导入的:我们在使用动态链接库DLL中暴露出来的函数的时候,可以直接使用暴露的函数,也可以通过显示地导入函数,编译器会产生质量更好的代码。由于编译器确切地知道了一个函数是否在一个DLL中,它就可以产生更好的代码,不再需要间接的调用转接。如下所示:

//显式的导入dll中暴露出来的函数
__declspec(dllimport) void func1(void);
int main(void)
{func1();
}

后面会专门讲如何使用 __declspec 来创建动态库。

(2)使用"xxx.def"文件来声明导入和导出符号

我们也可以不使用__declspec 来创建动态库,我们就按照正常的程序编写,如第一章节里面的 myMath.h 和 myMath.cpp 里面的内容,然后显示的添加一个 xxx.def 文件,怎么添加呢?

如下:右击项目/添加/新建项,选择如下的文件:

默认是使用source.def 我们可以自定义名称。关于这个def文件是如何规定哪些内容是导出的,哪一些是不导出的,这里暂时先不说明了,可以参考下面的几篇文章:

https://blog.csdn.net/qwq1503/article/details/85696279

http://www.cnblogs.com/enterBeijingThreetimes/archive/2010/08/04/1792099.html

2.2 使用 __declspec 来创建动态库的完整过程

(1)新建一个空项目或者是使用DLL模板都可以。

  • 添加头文件 framework.h
//framework.h
#pragma once#define WIN32_LEAN_AND_MEAN             // Exclude rarely-used stuff from Windows headers
// Windows Header Files
#include <windows.h>namespace mycal {__declspec(dllexport) int add(int a, int b);__declspec(dllexport) int sub(int a, int b);
}

注意:实际上就是在需要定义的函数前面添加一个 __declspec(dllexport) 是两个短下划线开头哦!来表示这两个函数是暴露出来可以供直接使用的,没有暴露的函数,在动态链接库中是没办法使用的。

2)实现函数的内容。

  • 定义一个 framework.cpp文件
//framework.cpp
#include "framework.h"int mycal::add(int x, int y)
{return x + y;
}int mycal::sub(int x, int y)
{return x - y;
}

这个地方和我们平时的实现完全一样,实现的时候不再需要添加 __declspec(dllexport) 了。

(3)生成项目

比如我选择生成 Debug x64位的结果,

生成之后得到如下的结果:

我们发现有一对配套的 xxx.dll 和 xxx.lib 文件,他们的大小不一样哦!此lib文件只是dll文件中导出函数的声明和定位信息,并不包含函数的实现,因此此lib文件只是在调用对应dll库的工程编译时使用,不需要随exe发布。

三、动态链接库的使用

3.1 静态调用dll

静态调用是由编译系统完成对dll文件的加载和应用程序结束时完成对dll的卸载,当调用某dll的应用程序结束时,则windows系统对该dll的应用记录减1,直到使用该dll的所有应用程序都结束,即对该dll应用记录为0,操作系统会卸载该dll,静态调用方法简单,但不如动态调用适用。

前面说了,window上生成的动态链接库的使用需要三个东西:头文件,dll文件,与dll对应的lib文件。

这里都具备了,现在新建一个项目,如何配置呢?

三步走配置

(1)第一步:配置包含路径——即头文件所在的路径,将头文件复制过来。

(2)第二步:配置库路径——即lib所在的路径,将Dll_demo_1.lib也复制到当前工程文件夹中。

(3)第三步:添加链接,——将上面得到的Dll_demo_1.lib添加到链接器:

#pragma comment(lib,"Dll_demo_1.lib")

这一句是告诉编译器与该dll相对应的.lib文件所在的路径和文件名。在生成dll文件时,链接器会自动为其生成一个对应的.lib文件,该文件包含了dll导出函数的符号名和序号(并没有实际的代码)。在应用程序中,.lib文件将作为dll的替代文件参与编译,编译完成后,.lib文件就不需要了。

修改复制过来的 framework.h,将里面的__declspec(dllexport)为__declspec(dllimport):

#pragma once
#define WIN32_LEAN_AND_MEAN             // Exclude rarely-used stuff from Windows headers
// Windows Header Files
#include <windows.h>namespace mycal {__declspec(dllimport) int add(int a, int b);__declspec(dllimport) int sub(int a, int b);
}

Source.cpp 代码如下:

#include <iostream>
#include <myMath.h> //添加自己定义的头
#pragma comment(lib,"Dll_demo_1.lib")
int main()
{int x = 50;int y = 10;int a,b;a = mycal::add(x, y);b = mycal::sub(x, y);printf("%d %d\n", a, b);getchar();return 0;
}

现在生成,生成没有错误,但是运行依然会报错,因为运行的时候需动态链接库,所以还需要配置动态链接库。

一共有三种方式,本文采用最简单的方式,直接将动态链接库和可执行exe文件拷贝到一起即可。

然后运行上面的程序,得到结果如下:


上面就是整个动态链接库的创建以及使用的过程。

注意1

1)可以修改.lib文件的文件名,只要在项目引用它时,使用它目前的名称,便可以正确运行,但不能改变.dll文件的名字,不然也会出现找不到.dll文件的错误

2).dll文件必须和.exe放在一起,.exe文件在哪里.dll文件也得在那里,两者“共存亡”,否则就会出现找不到.dll文件的错误

3)和静态库一样,.dll文件也要和应用程序的位数相对应,要么都是64位的,要么都是32位的,不可交叉使用。

4)静态调用不需要使用Win32API函数来加载和卸载Dll以及获取Dll中导出函数的地址,这是因为当通过静态链接方式编译生成程序时,编译器会将.lib文件中导出函数的函数符号链接到生成的exe文件中,.lib文件中包含的与之对应的dll文件的文件名也被编译存储在exe文件内部,当应用程序运行过程中需要加载dll文件时,windows将根据这些信息查找并加载dll,然后通过符号名实现对dll函数的动态链接,这样,exe将能直接通过函数名调用dll 的输出函数,就像调用程序内部的其他函数一样。

3.2 动态调用dll

动态调用是由程序员调用系统API函数加载和卸载dll,程序员可以决定dll文件何时加载,何时卸载,加载哪个dll文件,将dll文件的使用权完全交给程序员。

1)、新建控制台项目,添加 main.cpp 文件,将刚刚生成的 Dll_demo_1.dll 文件拷贝到项目目录下,main.cpp 代码如下

#include "stdio.h"
#include <windows.h>typedef int (*lpAddFun)(int ,int );//宏定义函数指针类型int main()
{HINSTANCE hDll;//DLL 句柄lpAddFun addFun;//函数指针hDll = LoadLibrary(L"Dll_demo_1.dll");//动态获取dll文件的路径if (hDll!=NULL){addFun =(lpAddFun)GetProcAddress(hDll,"add");//根据函数名在dll文件中获取该函数的地址    if (addFun!=NULL){int result =addFun(2,3);printf("2+3=%d",result);}FreeLibrary(hDll);}return 0;
}

运行结果:2+3=5

3.2.1 main.cpp分析

语句typedef int (*lpAddFun)(int ,int )定义了一个与add函数接收参数类型和返回值均相同的函数指针类型,随后在main函数中定义了lpAddFun的实例addFun;在函数main中定义了一个DLL HISTANCE句柄实例hDll,通过Win32API函数LoadLibrary动态加载DLL模块并将DLL模块句柄赋给hDll
main函数中通过Win32API函数GetProcAddress得到所加载的DLL模块中函数add的地址并赋值给addFun,经由函数指针addFun进行了对该DLLadd函数的调用;
在完成对dll的调用后,在main函数中通过Win32API函数FreeLibrary释放已加载的DLL模块。

通过以上的分析可知:
(a) 动态调用只需要dll文件即可,不需要对应的.h头文件和.lib文件,一般情况下,只要有dll,就可以调用此dll中的导出函数。
(b) 在调用dll中的函数时,需要知道导出函数的函数签名,若拥有dll对应的头文件,可以参照头文件即可,若没有头文件,使用特定工具也可以得到dll中导出函数的函数签名
(c) DLL需要已某种特定的方式声明导出函数。
(d) 应用程序需要以特定的方式调用DLL的淡出函数。

四、Visual Studio提供了一个命令行工具:Dumpbin

这个工具不需要自己安装,我们通过安装VS,可以直接使用,如下打开VS自带的命令行工具,如下:

这个工具有什么作用呢?简而言之,它可以查看一个 lib文件 dll文件提供了哪一些函数,暴露出来可供使用的,它还可以查看 exe 文件包含了哪一些静态库和动态库。

查看一下它的帮助信息:

如何使用呢?举几个简单的例子,以本文所创建的动态库和可执行程序作为演示:

(1)查看Dll_demo_1.dll 暴露出来了哪一些函数可以使用——dumpbin -exports xxxx.dll(xxxx.lib文件也一样的)

总结:

查看导入:dumpbin -exports 文件名称

查看导出:dumpbin -imports 文件名称


  1. https://blog.csdn.net/qq_33757398/article/details/81545966 ↩︎ ↩︎

Windows动态链接库相关推荐

  1. Windows动态链接库(dll)浅析 - 2

    Windows动态链接库(dll)浅析 - 2 Windows动态链接库(dll)浅析 - 1 Windows动态链接库(dll)浅析 - 3 5. DLL的编写 5.1 一个简单的dll项目 上面用 ...

  2. Windows 动态链接库 DLL 浅析

    一.概念 DLL:Dynamic Link Library,即动态链接库,这种库包含了可由多个程序同时使用的代码和数据. 它是microsoft在windows操作系统中实现共享函数库概念的一种实现方 ...

  3. Windows 动态链接库

    深入浅出Visual C++动态链接库(DLL)编程 作者:宋宝华 博客:http://blog.donews.com/21cnbao/ 原文链接:http://soft.yesky.com/less ...

  4. Windows动态链接库使用详解

    目录 概念 生成dll文件 导出函数 使用方法 概念 C++共享函数库有两种方式,动态库和静态库,动态链接库提供了一种方法,使进程可以调用不属于其可执行代码的函数.函数的可执行代码位于一个 DLL 文 ...

  5. Windows 动态链接库DLL浅解

    为什么80%的码农都做不了架构师?>>>    动态链接库(DLL),即:Dynamic Link Library.一个包含可由多个程序同时使用的代码和数据的库,DLL不是可执行文件 ...

  6. windows动态链接库dll生成和使用

    一.生成动态链接库. MyDll.h #pragma once#ifdef _MYDLL_EXPORT#define DLL_API _declspec(dllexport)#else#define ...

  7. java 动态链接_菜鸟提问:java 调用不了Windows动态链接库方式出错

    调用方式:LedControl INSTANCE = (LedControl) Native.loadLibrary("LEDSender2010",LedControl.clas ...

  8. Gazebo添加模型并控制模型运动作为动态障碍物(Ubuntu16.04, Gazebo7.16),附录动态链接库和静态链接库区别

    Gazebo作为一个运动仿真环境,可以直接加载编写好的机器人模型(如TIAGo和Yumi等),也可以自己构建多个运动模型,不过稍有难度.在构建复杂运动模型前,我们需要熟悉gazebo模型设置以及插件编 ...

  9. Windows 全局钩子 Hook 详解

    监控程序的实现       我们发现一些木马或其他病毒程序常常会将我们的键盘或鼠标的操作消息记录下来然后再将它发到他们指定的地方以实现监听.这种功能其他是利用了全局钩子将鼠标或键盘消息进行了截取,从而 ...

最新文章

  1. Regex Tester (免费有用)
  2. NHibernate教程(5)--CRUD操作
  3. 怎么用命令开远程主机的telnet服务 2
  4. web command line : http://yubnub.org/
  5. 汇编入门之输入、输出、奇偶判断、多字节变量定义
  6. ARMV8 datasheet学习笔记3:AArch64应用级体系结构之Memory order
  7. 【干货】神策标签生产引擎架构
  8. 腾讯-004-两个排序数组的中位数
  9. 今天下午又是在教室里坐了一个下午,头有点晕
  10. Java final关键字详解
  11. web项目01-----项目需求分析,需求文档
  12. 信创办公--基于WPS的Word最佳实践系列(图片背景的删除)
  13. 李少白讲摄影-不放过一切光线地坛书市新书首发圆满结束
  14. MediCool天使投资计划
  15. 信息安全 数据赛 铁人三项_2018.5.18信息安全铁人三项赛数据赛题解
  16. Jquery给HTML元素绑定按键事件-回车事件
  17. bmob php支付,个人开发者也能盈利!Bmob支付SDK使用实例
  18. 【Windows】联想win10系统截屏快捷键
  19. \加密与解密应用\使用AIDE做入侵检测\扫描与抓包分析\加密与解密应用
  20. 移动端热更新方案(iOS+Android)

热门文章

  1. live555 linux编译静态库,ubuntu live555测试
  2. cmd 下mysql常用的数据库命令
  3. 第三章 计算机图形处理,计算机图形学第三章-1(Basic).ppt
  4. 百度地图 添加工具条、比例尺控件
  5. oracle scott密码忘记,oracle忘记sys/system/scott用户的密码怎么办
  6. 利用位与运算简化代码
  7. Spring注入方式
  8. 用C++来设计开发的基于boost文档的站内搜索引擎项目,点赞收藏起来!
  9. 世界排名前十位的奢侈品
  10. 合约安全(1):Balancer中通货紧缩货币的套利攻击详解