第九讲 动态链接库
- 格式:ppt
- 大小:407.00 KB
- 文档页数:42
动态链接库的使用方法动态链接库(Dynamic Link Library,DLL)是Windows系统中一种常见的文件类型,用于存储可被程序在运行时动态加载的函数和数据。
它可以提供代码和资源的共享,使得程序的安装包更小,节省了系统资源。
使用动态链接库有以下几个优点:1.模块化:将程序代码和资源划分为独立的模块,便于开发和维护。
2.共享性:多个程序可以共享同一个动态链接库,减少重复的代码和数据的存储。
3.动态加载:可以在程序运行时动态地加载和卸载动态链接库,提高了程序的灵活性和可扩展性。
1.创建动态链接库:使用C/C++编程语言可以创建动态链接库。
首先,在开发环境中创建新的DLL项目,并选择动态链接库的类型。
在项目中添加需要的代码和资源,并编写相应的函数和数据接口。
将这些接口封装在一个头文件中,并在源文件中实现具体的功能。
最后,编译项目生成动态链接库文件(.dll 文件)。
2.导出函数和数据:在动态链接库中,明确指定哪些函数和数据需要被其他程序调用。
在函数和数据的声明前加上__declspec(dllexport)关键字即可。
例如:```C++__declspec(dllexport) int Add(int a, int b);```3.调用动态链接库:在其他程序中调用动态链接库中的函数和数据,需要先导入相应的函数和数据。
使用C/C++编程语言可以创建一个头文件,其中包含要导入的函数和数据的声明。
例如:```C++__declspec(dllimport) int Add(int a, int b);__declspec(dllimport) extern double PI;```然后,在使用这些函数和数据的源文件中包含这个头文件即可。
4.加载和卸载动态链接库:在程序运行时,需要动态地加载动态链接库,并在使用完之后卸载。
可以使用LoadLibrary函数来加载动态链接库,使用FreeLibrary函数来卸载动态链接库。
什么是动态链接库?一、动态链接库的概念动态链接库(Dynamic Link Library ,缩写为DLL )是一个可以被其它应用程序共享的程序模块,其中封装了一些可以被共享的例程和资源。
动态链接库文件的扩展名一般是dll ,也有可能是drv 、sys 和fon ,它和可执行文件(exe )非常类似,区别在于DLL 中虽然包含了可执行代码却不能单独执行,而应由Windows 应用程序直接或间接调用。
动态链接是相对于静态链接而言的。
所谓静态链接是指把要调用的函数或者过程链接到可执行文件中,成为可执行文件的一部分。
换句话说,函数和过程的代码就在程序的exe 文件中,该文件包含了运行时所需的全部代码。
当多个程序都调用相同函数时,内存中就会存在这个函数的多个拷贝,这样就浪费了宝贵的内存资源。
而动态链接所调用的函数代码并没有被拷贝到应用程序的可执行文件中去,而是仅仅在其中加入了所调用函数的描述信息(往往是一些重定位信息)。
仅当应用程序被装入内存开始运行时,在Windows 的管理下,才在应用程序与相应的DLL 之间建立链接关系。
当要执行所调用DLL 中的函数时,根据链接产生的重定位信息,Windows 才转去执行DLL 中相应的函数代码。
一般情况下,如果一个应用程序使用了动态链接库,Win32 系统保证内存中只有DLL 的一份复制品,这是通过内存映射文件实现的。
DLL 首先被调入Win32 系统的全局堆栈,然后映射到调用这个DLL 的进程地址空间。
在Win32 系统中,每个进程拥有自己的32 位线性地址空间,如果一个DLL 被多个进程调用,每个进程都会收到该DLL 的一份映像。
与16 位Windows 不同,在Win32 中DLL 可以看作是每个进程自己的代码。
二、动态链接库的优点1 .共享代码、资源和数据使用DLL 的主要目的就是为了共享代码,DLL 的代码可以被所有的Windows 应用程序共享。
2 .隐藏实现的细节DLL 中的例程可以被应用程序访问,而应用程序并不知道这些例程的细节。
动态链接库的使用一、编写合格的动态链接库头文件C语言的头文件,可供一个或多个程序引用,里面一般定义程序所需的常量,自定义类型及函数原型说明等.其中的函数原型说明,则供编译器检查语法,用于排除引用参数时类型不一致的错误.只有编写合格的动态链接库头文件,程序员才能正确使用动态链接库内的函数.动态链接库头文件要采用C语言标准格式,其中的动态函数原型定义,不必象上文介绍的那样用(*动态函数名)的描述形式.请看下面的例子:(每行开始的数字为所在行行号,为笔者添加,供注解使用)1 /* adatetime.h : 纵横软件制作中心雨亦奇(zhsoft@)编写, 2002-03-06. */23 #ifndef __DATETIME_H45 #define __DATETIME_H67 /* 日期结构 */8 typedef struct9 {10 int year;11 int mon;12 int day;13 }DATETYPE;1415 /* 时间结构 */16 typedef struct17 {18 char hour;19 char min;20 char sec;21 }TIMETYPE;2223 int getdate(DATETYPE *d); /* 取当前日期 */24 int gettime(TIMETYPE *t); /* 取当前时间 */2526 #endif注:与上文的datetime.h文件比较,从该头文件第23,24行可以看到,动态函数getdate,gettime的原型定义改变了,不再使用(*getdate),(*gettime)的格式了(这种格式使用较为罗嗦).二、正确编译与命名动态链接库为了让GCC编译器生成动态链接库,编译时须加选项-shared.(这点须牢记)LINUX系统中,为了让动态链接库能被系统中其它程序共享,其名字应符合“lib*.so*”这种格式.如果某个动态链接库不符合此格式,则LINUX的动态链接库自动装入程序(ld.so)将搜索不到此链接库,其它程序也无法共享之.格式中,第一个*通常表示为简写的库名,第二个*通常表示为该库的版本号.如:在我的系统中,基本C动态链接库的名字为libc.so.6,线程pthread动态链接库的名字为libpthread.so.0等等.本文例子所生成的动态链接库的名字为libmy.so,虽没有版本号,但也符合所要求的格式.生成该动态链接库的维护文件makefile-lib内容如下:1 # makefile : 纵横软件制作中心雨亦奇编写, 2002-03-07.23 all : libmy.so45 SRC = getdate.c gettime.c67 TGT = $(SRC:.c=.o)89 $(SRC) : adatetime.h10 @touch $@1112 %.o : %.c13 cc -c $?1415 # 动态链接库(libmy.so)生成16 libmy.so : $(TGT)17 cc -s -shared -o $@ $(TGT)18运行命令:$ make -f makefile-lib$即生成libmy.so库.注: 维护文件中,第17行用-shared选项以生成动态链接库,用-s选项以去掉目标文件中的符号表,从而减小文件长度.三、共享动态链接库3.1 动态链接库配置文件为了让动态链接库为系统所使用,需要维护动态链接库的配置文件 /etc/ld.so.conf 。
什么是动态链接库?什么是dll:dll只是一组源代码的模块,每个模块包含一些可供应用程序或者其他dll 调用的函数,在应用程序调用一个dll里面的函数的时候,操作系统会将dll的文件映像映射到进程的地址空间中,这样进程中所有的线程就可以调用dll中的函数了dll加载完成后,这个时候dll对于进程中的线程来说只是一些被放在地址进程空间附加的代码和数据,操作系统为了节省内存空间,同一个dll在内存中只有一个,也就是说如果你的的两个应用程序都需要加载user32.dll,那么操作系统也只会加载一次user32.dll到内存中因为代码段在内存中的权限都是为只读的,所以当多个应用程序加载同一个dll的时候,不用担心应用程序会修改dll的代码段。
当线程调用dll的一个函数,函数会在线程栈中取得传递给他的参数,并使用线程栈来存放他需要的变量,dll函数创建的任何对象都为调用线程或者调用进程拥有,dll不会拥有任何对象,也就是说如果dll中的一个函数调用了VirtualAlloc,系统会从调用进程的地址空间预定地址,即使撤销了对dll的映射,调用进程的预定地址依然会存在,直到用户取消预定或者进程结束。
示例代码:mylib.h1 #ifdef MYLIBAPI2 #else3 #define MYLIBAPI extern "C" __desclspec(dllimport)4 #endif56 MYLIBAPI int g_nResult;78 MYLIBAPI int Add(int nLeft,int nRight)mylib.cpp1 #include <windows.h>23 #define MYLIBAPI extern "C" __declspec(dllexport)4 #include "mylib.h"5 int g_nResult;67 int Add(int nLeft,int nRight)8 {9 g_nResult = nLeft + nRight;10 return g_nResult;11 }输入命令:cl /LDd mylib.cpp可以生成可供调试的dll这个时候会多出四个文件,分别是mylib.exp,mylib.lib,mylib.dll,mylib.objmylib.obj保存的是在链接器生成dll的需要的信息当链接器检测到应用程序导出了一个函数或者变量,链接器就会生成mylib.lib文件,这个只是列出了导出的函数和变量的符号名输入命令查看lib里面的导出段dumpbin -exports mylib.lib我们可以看到这个lib里面export了_Add和_g_nResult如果我们使用dumpbin -imports mylib.libimports里面没有变量或者函数,这是因为lib里面记录的只是导出的函数和变量,只有在声明有导出函数或者变量的时候,才会生成这个文件mylib.dll则是我们最终生成的模块如果使用dumpbin查看mylib.dll的导出导入则因为太多,所以不贴出来到我们需要将一个函数导出的时候,可以使用__desclspec(dllexport)来声明为导出函数,需要从dll使用一个函数的时候,可以使用__desclspecc(dllimport)来前置声明一个函数,当然,也可以不使用import前置声明,但是使用improt可以明确告诉编译器这些函数是从dll导入的,提高效率什么是导出?当将函数或者变量声明为导出后,编译器在生成obj的时候会嵌入一些额外的信息,以便于让链接器在生成dll的时候使用,并且会生成一个记录导出函数和变量的lib文件,在生成可执行文件的时候,我们需要通过链接这个lib来取得dll的一些信息,链接器在生成dll的时候,会在dll文件中嵌入一个导出符号表,这个符号表记录了导出的函数和变量的符号名,并且保存对应的文件偏移量地址,这样当可执行文件需要调用dll里面的函数的时候,可以通过这个符号表来找到对应函数的地址最后我们开始构建可执行文件,代码如下myexe.cpp#include <cstdio>#include "mylib.h"int main(void){int nLeft = 10,nRight = 20;printf("%d\n",Add(nLeft,nRight));}cl myexe.cpp mylib.lib我们在编译的时候一定要链接mylib.lib,这样编译器才知道要到哪里去找mylib的变量和函数相关信息,并且可执行文件也才知道程序需要mylib.dll这个dll,这样程序在加载的时候会搜索用户磁盘上的dll,如果没找到则会报错,找到则将dll映射到进程的内存空间里面当dll映射到进程的内存空间里面后,加载程序会查看在对应的dll的导出段符号是否存在,如果不存在,则报错,如果存在,那么加载程序会将该符号加载到该符号的所在的文件偏移量(RVA,虚拟地址,但在dll里面实际上是该符号所在文件的位置),加上该dll加载的虚拟地址,保存到可执行程序的导入段中,当代码引用到导入符号的时候,可执行文件会去查看导入段并且得到导入符号的地址,这样就能访问导入的变量或者函数。
动态链接库的原理及使用动态链接库(Dynamic Link Library,简称DLL)是一种用于在Windows操作系统中共享程序代码和资源的文件格式。
DLL可以包含多个函数和数据,它们可以被多个应用程序同时使用,提供了一种更加灵活、高效的代码共享方式,使得代码的复用和维护更加方便。
DLL的原理是通过动态链接的方式将DLL文件中的函数和数据加载到内存中,然后在需要使用这些函数和数据的应用程序中进行调用。
这样做的好处是可以减少程序的体积,减少了重复代码的占用空间,提高了程序的运行效率。
DLL的使用分为两个步骤:编写和生成DLL文件,以及在应用程序中调用DLL中的函数和数据。
编写和生成DLL文件的过程通常是使用特定的开发工具或编程语言进行操作。
编写DLL文件时,需要定义导出函数,即可以被其他应用程序调用的函数。
在C/C++语言中,可以使用__declspec(dllexport)关键字来进行函数的导出声明。
生成DLL文件的过程也有多种方式,如使用编译器提供的选项进行生成,或者使用特定的构建工具进行生成。
在应用程序中调用DLL的函数和数据时,首先需要通过LoadLibrary 函数将DLL文件加载到内存中,然后使用GetProcAddress函数获取要调用的函数的地址。
获取到函数地址后,就可以像调用本地函数一样调用DLL中的函数了。
如果DLL中还有需要使用的数据,也可以通过导出的全局变量或者提供的函数来获取和使用。
除了使用LoadLibrary和GetProcAddress函数之外,Windows API 中还提供了一些使用DLL的高级函数调用方式,如使用COM组件、使用注册表等。
1.代码复用:多个应用程序可以共享同一个DLL文件,避免了代码的重复编写,提高了代码的复用性。
2.节省内存:多个应用程序共享一个DLL文件时,DLL中的代码和数据只需要在内存中加载一次,减少了内存的占用。
3.程序的灵活性:使用DLL可以实现模块化的设计和开发,提高了程序的灵活性和可维护性。
动态链接库1、动态链接库概述动态链接库一直就是Windows操作系统的基础。
动态链接库通常都不能直接运行,也不能接收消息。
它们是一些独立的文件,其中包含能被可执行程序或其他DLL调用来完成某项工作的函数。
只有在其他模块调用动态链接库中的函数时,它才发挥作用。
在实际编程时,我们可以把完成某种功能的函数放在一个动态链接库中,然后供给其他程序调用Windows API中的所有函数都包含在DLL中,其中有3个最重要的DLLKernel32.dll:包含那些用于管理内存、进程和线程的函数User32.dll:包含那些用于执行用户界面任务的函数GDI32.dll:包含那些用于画图和显示文本的函数1.1、静态库与动态库静态库:函数和数据被编译进一个二进制文件(扩展名.lib)。
在使用静态库的情况下,在编译链接可执行文件时,链接器从库中复制这些函数和数据并把它们和应用程序的其他模块组合起来创建最终的可执行文件(.exe文件)。
当发布产品时,只需要发布这个可执行文件,并不需要发布使用的静态库动态库:在使用动态库时,往往提供两个文件:一个引入库(.lib)文件和一个DLL(.dll)文件。
虽然引入库文件的后缀名也是“lib”,但是,动态链接库的引入库文件和静态库文件有着本质的区别,对一个DLL来说,其引入库文件(.lib)包含该DLL导出的函数和变量的符号名,而.dll文件包含该DLL实际的函数和数据。
在使用动态库的情况下,在编译链接可执行文件时,只需要链接该DLL的引入库文件,该DLL中的函数代码和数据并不复制到可执行文件中,直到可执行程序运行时,才去加载所需的DLL,将该DLL映射到进程的地址空间中,然后访问DLL中导出的函数。
这时,在发布产品时,除了发布可执行文件意外,同时还要发布该程序将要调用的动态链接库1.2、使用动态链接库的好处1、可以采用多种语言来编写2、增强产品的功能3、提供二次开发的平台4、简化项目管理5、可以节省磁盘空间和内存当进程被加载时,系统为它分配一个4GB的地址空间,接着分析该可执行模块,找到该程序要调用哪些DLL,然后系统搜索这些DLL,找到后就加载它们,并为它们分配虚拟的内存空间,然后将DLL的页面映射到调用进程的地址空间。
1. 什么是lib文件,lib和dll的关系如何(1)lib是编译时需要的,dll是运行时需要的。
如果要完成源代码的编译,有lib就够了。
如果也使动态连接的程序运行起来,有dll就够了。
在开发和调试阶段,当然最好都有。
(2)一般的动态库程序有lib文件和dll文件。
lib文件是必须在编译期就连接到应用程序中的,而dll文件是运行期才会被调用的。
如果有dll文件,那么对应的lib文件一般是一些索引信息,具体的实现在dll文件中。
如果只有lib文件,那么这个lib文件是静态编译出来的,索引和实现都在其中。
静态编译的lib文件有好处:给用户安装时就不需要再挂动态库了。
但也有缺点,就是导致应用程序比较大,而且失去了动态库的灵活性,在版本升级时,同时要发布新的应用程序才行。
(3)在动态库的情况下,有两个文件,一个是引入库(.LIB)文件,一个是DLL文件,引入库文件包含被DLL导出的函数的名称和位置,DLL包含实际的函数和数据,应用程序使用LIB文件链接到所需要使用的DLL文件,DLL 库中的函数和数据并不复制到可执行文件中,因此在应用程序的可执行文件中,存放的不是被调用的函数代码,而是DLL中所要调用的函数的内存地址,这样当一个或多个应用程序运行时再把程序代码和被调用的函数代码链接起来,从而节省了内存资源。
从上面的说明可以看出,DLL和.LIB文件必须随应用程序一起发行,否则应用程序将会产生错误。
2、严重警告:(1) 用 extern "C" _declspec(dllexport) 只可以导出全局函数,不能导出类的成员函数(2) 使用extern "C" _declspec(dllexport)输出的函数可以被c语言调用,否则则不可(3) 注意标准调用约定的问题,输出与调用的函数约定应该一致,如当dll 模块的函数输出采用标准调用约定_stdcall,则调用程序的导入函数说明也要用标准约定(4) 用extern "C" _declspec(dllexport) 和 EXPOTRT导出的函数不改变函数名,可以给c++或c编写的exe调用.假如没有extern "C",导出的函数名将会改变,只能给c++编写的exe调用(5)在动态加载动态链接库函数时注意GetProcAddress(hInst,"add")中第二个参数是否为动态链接库导出的函数名,因为在生成动态库时可能会改变动态库导出函数的函数名,加上修饰符(6)dll初始化全局变量时,全局变量要放在共享数据断,并且初始化每一个变量,在StartHook函数里初始化其值,记得一进函数就初始化(7)调试时,编译器会自动查找其目录下(不含debug和release目录)的dll文件,所以dll文件应该放在主文件目录下,但生成的应用程序则只会在同一个目录下找dll(不需要lib文件),所以单纯的运行exe,不通过编译器,那就要把dll文件放在与exe相同的目录下(8)用#pragma comment(lib,"dllTest.lib")导入lib文件,不需要在设置里修改(9) dll里的指针变量不要用newDLL 调用方式DLL(动态连接库),可以分为动态调用于静态调用。