动态链接库DLL
2016-05-08 23:20
253 查看
一、DLL简介
它是Dynamic Link Library 的缩写形式,DLL 是一个包含可由多个程序同时使用的代码和数据的库,DLL不是可执行文件。动态链接提供了一种方法,使进程可以调用不属于其可执行代码的函数。函数的可执行代码位于一个 DLL中,该 DLL 包含一个或多个已被编译、链接并与使用它们的进程分开存储的函数。DLL 还有助于共享数据和资源。多个应用程序可同时访问内存中单个DLL 副本的内容。DLL 是一个包含可由多个程序同时使用的代码和数据的库。例如,在 Windows 操作系统中,Comdlg32
DLL 执行与对话框有关的常见函数。因此,每个程序都可以使用该 DLL 中包含的功能来实现“打开”对话框。这有助于促进代码重用和内存的有效使用。
通过使用 DLL,程序可以实现模块化,由相对独立的组件组成。例如,一个计帐程序可以按模块来销售。可以在运行时将各个模块加载到主程序中(如果安装了相应模块)。因为模块是彼此独立的,所以程序的加载速度更快,而且模块只在相应的功能被请求时才加载。
此外,可以更为容易地将更新应用于各个模块,而不会影响该程序的其他部分。例如,您可能具有一个工资计算程序,而税率每年都会更改。当这些更改被隔离到 DLL 中以后,您无需重新生成或安装整个程序就可以应用更新。
与动态链接库相对应,还有一种称之为静态链接库的函数库,两者的主要区别在于使用方法上面。
静态连接库就是把(lib)文件中用到的函数代码直接链接进目标程序,程序运行的时候不再需要其它的库文件;动态链接就是把调用的函数所在文件模块(DLL)和调用函数在文件中的位置等信息链接进目标程序,程序运行的时候再从DLL中寻找相应函数代码,因此需要相应DLL文件的支持。
静态链接库与动态链接库都是共享代码的方式,如果采用静态链接库,则无论你愿不愿意,lib 中的指令都全部被直接包含在最终生成的 EXE 文件中了。但是若使用 DLL,该 DLL 不必被包含在最终 EXE 文件中,EXE 文件执行时可以“动态”地引用和卸载这个与 EXE 独立的 DLL 文件。静态链接库和动态链接库的另外一个区别在于静态链接库中不能再包含其他的动态链接库或者静态库,而在动态链接库中还可以再包含其他的动态或静态链接库。
“每一个lib文件就是若干函数(假设只有函数)的定义”
lib库有两种,一种是包含了函数所在DLL文件和文件中函数位置的信息,称为导出库;一种是包含函数代码本身,一般现有的DLL,用的是前一种库
lib有静态lib和动态lib之分。
静态lib将导出声明和实现都放在lib中。编译后所有代码都嵌入到宿主程序
动态lib相当于一个h文件,是对实现部分(.dll文件)的导出部分的声明。编译后只是将导出声明部分编译到宿主程序中,运行时候需要相应的dll文件支持
“通过#include包含这些函数声明的头文件后,我们的应用程序就可以使用lib文件中的函数”
以下省略一千多字的DLL优缺点分析。如果又兴趣,百度之。
按照DLL是否使用MFC,可以将DLL分为两种:普通的Win32的DLL和基于MFC的DLL。DLL文件一般具备以下三个文件:包含文件、导入库文件、实际代码文件,它们对应的扩展名分别是:.H、.LIB、.DLL。
dll 两种链接方式 : 动态链接和静态链接(链接亦称加载)
动态链接是指在生成可执行文件时不将所有程序用到的函数链接到一个文件,因为有许多函数在操作系统带的dll文件中,当程序运行时直接从操作系统中找。
而静态链接就是把所有用到的函数全部链接到exe文件中。
动态链接是只建立一个引用的接口,而真正的代码和数据存放在另外的可执行模块中,在运行时再装入;
而静态链接是把所有的代码和数据都复制到本模块中,运行时就不再需要库了。
1.生成 静态链接库 lib 和动态链接库 dll
新建工程 (newdll) win32项目 -> dll
添加.h文件
betabinlib.h
[cpp] view
plain copy
#ifndef BETABINLIB_H
#define BETABINLIB_H
#ifdef NEWDLL_EXPORTS //自动添加的宏 右键工程-属性-配置属性-预处理器-..定义
#define MYDLL_API extern "C" __declspec(dllexport)
#else
#define MYDLL_API extern "C" __declspec(dllimport)
#endif
MYDLL_API int add(int x, int y); // 必须加前缀
#endif
添加.cpp文件 betabinlib.cpp
[cpp] view
plain copy
#include "stdafx.h"
#include "betabinlib.h"
int add(int x, int y)
{
return x + y;
}
编译生成 .dll 和 .lib文件
2.使用
(1)dll的静态加载--将整个dll文件 加载到 .exe文件中
特点:程序较大,占用内存较大,但速度较快(免去 调用函数LOADLIB等)
测试:
需要 .lib 和 .dll两个文件 (.lib 做
引导用),.h文件
main.cpp
[cpp] view
plain copy
#include <stdio.h>
#include "betabinlib.h"
#include <Windows.h>
#pragma comment(lib, "newdll.lib")
int main()
{
printf("2 + 3 = %d \n", add(2, 3));
return 0;
}
(2) dll的动态加载--根据需要加载响应函数,随时可卸载。不会因为找不到dll, 导致程序不能运行(需要自己做判断处理)。
只需要 .lib文件,不需要 .h文件
main.cpp
[cpp] view
plain copy
#include <stdio.h>
#include <Windows.h>
int main()
{
HINSTANCE h=LoadLibraryA("newdll.dll");
typedef int (* FunPtr)(int a,int b);//定义函数指针
if(h == NULL)
{
FreeLibrary(h);
printf("load lib error\n");
}
else
{
FunPtr funPtr = (FunPtr)GetProcAddress(h,"add");
if(funPtr != NULL)
{
int result = funPtr(3, 3);
printf("3 + 3 = %d \n", result);
}
else
{
printf("get process error\n");
printf("%d",GetLastError());
}
FreeLibrary(h);
}
return 0;
}
它是microsoft在windows操作系统中实现共享函数库概念的一种实现方式。其中windows中 一些作为DLL实现的文件有:
ActiveX控件(.ocx)文件:如windows上的日历控件。
控制面板(.cpl)文件:控制面板中的每一项都是一个专用的DLL。
设备驱动程序(.drv)文件:如控制打印到打印机的打印机驱动程序。
(1)节省内存和代码重用:当多个程序使用同一个函数库时,DLL可以减少在磁盘和物理内存中加载代码的重复量,且有助于代码的重用。
(2)模块化:DLL有助于促进模块式程序开发。模块化允许仅仅更改几个应用程序共享使用的一个DLL中的代码和数据而不需要更改应用程序自身。这种模块话的基本形式允许如Microsoft Office、Microsoft Visual Studio、甚至windows自身这样大的应用程序 使用较为紧凑的补丁和服务包。
缺点:
DLL Hell:即DLL地狱,指几个应用程序在使用同一个共享的DLL库时发生版本冲突。
究其原因,八个字:成也共用,败也共用。因为DLL Hell正是由于动态链接库可与其他程序共用函数、资源所导致。
主要有两种情况:
设想这样一个场景:程序A会使用1.0版本的动态链接库X,则在程序A安装到系统时,会同时安装该1.0版本的动态链接库X。假设另一个程序B也会使用到动态链接库X,那么程序B直接复制到硬盘中即可正常运行,因为动态链接库已经存在于系统中。然而有一天,另一程序C也要使用动态链接库X,但是由于程序C开发的时间较晚,其需要较新版本---2.0版本的动态链接库X。则在程序C被安装到系统时,2.0版本的动态链接库X 也必须随之安装到系统中,此时系统中1.0版本的动态链接库将被2.0版本所取代(替换)。
情况1:新版本的动态链接库不兼容旧版本。如,A何B需要X所提供的功能,在升级到2.0后,新版本的X竟然把此功能取消了(很难想象吧,呵呵但有时候就是如此....)。则此时虽然C能正常运行,但A和B均无法工作了。
情况2:新版本的动态链接库兼容旧版本,但是存在一个bug。
可看下面的例子(仅仅为了说明问题):
[cpp] view
plain copy
print?
// X1.0 version
void func(int count)
{
if(count < 0)
count = 0;
....
}
// X2.0 version
void func(int count)
{
//负数处理被移除!
...
}
一旦出现count为负数的情况,则程序A在新版本的处理下就会有问题。
解决办法:Side-by-side Assembly,是windows Xp以及以上系统解决动态链接库版本冲突所使用的技术,重点在于编译程序时,由VS生成一个manifest文件,指明当前应用程序所使用的动态链接库版本号;发布程序时需同时发布该manifest文件,供客户计算机上的DLL
Loader根据manifest加载适当版本的DLL,若不发布该项manifest,客户机则按默认版本加载DLL。下图为其典型的场景:
其实没那么简单! lib也有静态lib和动态lib之分。
静态lib:它将导出声明(后面会讲)和实现均放到lib中,编译后所有代码都嵌入到宿主程序中去。
动态lib:相当于一个h文件,它是对实现部分(.DLL)的导出部分的声明。编译后只是将导出声明部分编译到宿主程序中,运行时需要相应的DLL文件的支持,否则无法工作。当生成一个新的DLL时,也会有配套的lib产生(即二者需一起分发),此时的lib即为动态lib(后面会有还有实验)。
plain copy
print?
// mydll.h file
extern "C" _declspec(dllexport) int add(int a, int b);
//mydll.cpp file
#include "mydll.h"
int add(int a, int b) //该DLL需要导出的函数功能:加法
{
return a + b;
}
说明:
(1)前面的 extern “C” 告诉编译器函数可以在本模块或其他模块中使用,其中“C”表明需按照C语言方式编译和连接它,因为C++编译时,会对函数名进行修饰,用于实现函数重载,而C里面没有这个功能,所以需要用extern "C"在头文件进行声明的时候加以区分,以便链接时能进行正确地函数名查找。
(2)_declspec(dllexport)为导出函数关键字,意为需从DLL中导出该函数,以便使用。
注:需把对应的 .dll 文件以及.lib 文件和.h文件(结合方式时)拷贝至调用的程序目录下
plain copy
print?
#include<wtypes.h>
#include <winbase.h>
#include <iostream>
_declspec(dllimport) int Add(int a, int b); //导入声明,亦可以不加,如果加上可加快程序运行
typedef int(*pAdd)(int a,int b);
int main()
{
HINSTANCE hDLL;
pAdd Add;
hDLL=LoadLibrary("mydll.dll"); //加载 DLL文件
if(hDLL == NULL)std::cout<<"Error!!!\n";
Add=(pAdd)GetProcAddress(hDLL,"add"); //取DLL中的函数地址,以备调用
int a =Add(5,8);
std::cout<<"a: "<<a<<std::endl;
FreeLibrary(hDLL);
return 0;
}
输出结果:
plain copy
print?
#include<wtypes.h>
#include <winbase.h>
#include <iostream>
#include "mydll.h"
#pragma comment(lib,"mydll.lib") //将mydll.lib库文件连接到目标文件中(即本工程)
extern "C"_declspec(dllimport) int add(int a,int b);
int main()
{
int a =add(5,8);
std::cout<<"a: "<<a<<std::endl;
return 0;
}
输出:
反例演示:此时如果去掉 .dll 文件(即只有.lib 和 .h文件),则会出错:
它是Dynamic Link Library 的缩写形式,DLL 是一个包含可由多个程序同时使用的代码和数据的库,DLL不是可执行文件。动态链接提供了一种方法,使进程可以调用不属于其可执行代码的函数。函数的可执行代码位于一个 DLL中,该 DLL 包含一个或多个已被编译、链接并与使用它们的进程分开存储的函数。DLL 还有助于共享数据和资源。多个应用程序可同时访问内存中单个DLL 副本的内容。DLL 是一个包含可由多个程序同时使用的代码和数据的库。例如,在 Windows 操作系统中,Comdlg32
DLL 执行与对话框有关的常见函数。因此,每个程序都可以使用该 DLL 中包含的功能来实现“打开”对话框。这有助于促进代码重用和内存的有效使用。
通过使用 DLL,程序可以实现模块化,由相对独立的组件组成。例如,一个计帐程序可以按模块来销售。可以在运行时将各个模块加载到主程序中(如果安装了相应模块)。因为模块是彼此独立的,所以程序的加载速度更快,而且模块只在相应的功能被请求时才加载。
此外,可以更为容易地将更新应用于各个模块,而不会影响该程序的其他部分。例如,您可能具有一个工资计算程序,而税率每年都会更改。当这些更改被隔离到 DLL 中以后,您无需重新生成或安装整个程序就可以应用更新。
与动态链接库相对应,还有一种称之为静态链接库的函数库,两者的主要区别在于使用方法上面。
静态连接库就是把(lib)文件中用到的函数代码直接链接进目标程序,程序运行的时候不再需要其它的库文件;动态链接就是把调用的函数所在文件模块(DLL)和调用函数在文件中的位置等信息链接进目标程序,程序运行的时候再从DLL中寻找相应函数代码,因此需要相应DLL文件的支持。
静态链接库与动态链接库都是共享代码的方式,如果采用静态链接库,则无论你愿不愿意,lib 中的指令都全部被直接包含在最终生成的 EXE 文件中了。但是若使用 DLL,该 DLL 不必被包含在最终 EXE 文件中,EXE 文件执行时可以“动态”地引用和卸载这个与 EXE 独立的 DLL 文件。静态链接库和动态链接库的另外一个区别在于静态链接库中不能再包含其他的动态链接库或者静态库,而在动态链接库中还可以再包含其他的动态或静态链接库。
“每一个lib文件就是若干函数(假设只有函数)的定义”
lib库有两种,一种是包含了函数所在DLL文件和文件中函数位置的信息,称为导出库;一种是包含函数代码本身,一般现有的DLL,用的是前一种库
lib有静态lib和动态lib之分。
静态lib将导出声明和实现都放在lib中。编译后所有代码都嵌入到宿主程序
动态lib相当于一个h文件,是对实现部分(.dll文件)的导出部分的声明。编译后只是将导出声明部分编译到宿主程序中,运行时候需要相应的dll文件支持
“通过#include包含这些函数声明的头文件后,我们的应用程序就可以使用lib文件中的函数”
以下省略一千多字的DLL优缺点分析。如果又兴趣,百度之。
按照DLL是否使用MFC,可以将DLL分为两种:普通的Win32的DLL和基于MFC的DLL。DLL文件一般具备以下三个文件:包含文件、导入库文件、实际代码文件,它们对应的扩展名分别是:.H、.LIB、.DLL。
dll 两种链接方式 : 动态链接和静态链接(链接亦称加载)
动态链接是指在生成可执行文件时不将所有程序用到的函数链接到一个文件,因为有许多函数在操作系统带的dll文件中,当程序运行时直接从操作系统中找。
而静态链接就是把所有用到的函数全部链接到exe文件中。
动态链接是只建立一个引用的接口,而真正的代码和数据存放在另外的可执行模块中,在运行时再装入;
而静态链接是把所有的代码和数据都复制到本模块中,运行时就不再需要库了。
1.生成 静态链接库 lib 和动态链接库 dll
新建工程 (newdll) win32项目 -> dll
添加.h文件
betabinlib.h
[cpp] view
plain copy
#ifndef BETABINLIB_H
#define BETABINLIB_H
#ifdef NEWDLL_EXPORTS //自动添加的宏 右键工程-属性-配置属性-预处理器-..定义
#define MYDLL_API extern "C" __declspec(dllexport)
#else
#define MYDLL_API extern "C" __declspec(dllimport)
#endif
MYDLL_API int add(int x, int y); // 必须加前缀
#endif
添加.cpp文件 betabinlib.cpp
[cpp] view
plain copy
#include "stdafx.h"
#include "betabinlib.h"
int add(int x, int y)
{
return x + y;
}
编译生成 .dll 和 .lib文件
2.使用
(1)dll的静态加载--将整个dll文件 加载到 .exe文件中
特点:程序较大,占用内存较大,但速度较快(免去 调用函数LOADLIB等)
测试:
需要 .lib 和 .dll两个文件 (.lib 做
引导用),.h文件
main.cpp
[cpp] view
plain copy
#include <stdio.h>
#include "betabinlib.h"
#include <Windows.h>
#pragma comment(lib, "newdll.lib")
int main()
{
printf("2 + 3 = %d \n", add(2, 3));
return 0;
}
(2) dll的动态加载--根据需要加载响应函数,随时可卸载。不会因为找不到dll, 导致程序不能运行(需要自己做判断处理)。
只需要 .lib文件,不需要 .h文件
main.cpp
[cpp] view
plain copy
#include <stdio.h>
#include <Windows.h>
int main()
{
HINSTANCE h=LoadLibraryA("newdll.dll");
typedef int (* FunPtr)(int a,int b);//定义函数指针
if(h == NULL)
{
FreeLibrary(h);
printf("load lib error\n");
}
else
{
FunPtr funPtr = (FunPtr)GetProcAddress(h,"add");
if(funPtr != NULL)
{
int result = funPtr(3, 3);
printf("3 + 3 = %d \n", result);
}
else
{
printf("get process error\n");
printf("%d",GetLastError());
}
FreeLibrary(h);
}
return 0;
}
一、概念
DLL:Dynamic Link Library,即动态链接库,这种库包含了可由多个程序同时使用的代码和数据。它是microsoft在windows操作系统中实现共享函数库概念的一种实现方式。其中windows中 一些作为DLL实现的文件有:
ActiveX控件(.ocx)文件:如windows上的日历控件。
控制面板(.cpl)文件:控制面板中的每一项都是一个专用的DLL。
设备驱动程序(.drv)文件:如控制打印到打印机的打印机驱动程序。
二、由来
DLL最初用于节约应用程序所需要的磁盘和内存空间。早前,在传统的非共享库中,一部分代码简单地附加到调用的程序中。如果两个程序同时调用同一个子程序,就会出现两份那段代码。相反,许多应用共享的代码能够切分到一个DLL中,在硬盘上存为一个文档,在内存中只需使用一个实例。三、DLL的优缺点
优点:(1)节省内存和代码重用:当多个程序使用同一个函数库时,DLL可以减少在磁盘和物理内存中加载代码的重复量,且有助于代码的重用。
(2)模块化:DLL有助于促进模块式程序开发。模块化允许仅仅更改几个应用程序共享使用的一个DLL中的代码和数据而不需要更改应用程序自身。这种模块话的基本形式允许如Microsoft Office、Microsoft Visual Studio、甚至windows自身这样大的应用程序 使用较为紧凑的补丁和服务包。
缺点:
DLL Hell:即DLL地狱,指几个应用程序在使用同一个共享的DLL库时发生版本冲突。
究其原因,八个字:成也共用,败也共用。因为DLL Hell正是由于动态链接库可与其他程序共用函数、资源所导致。
主要有两种情况:
设想这样一个场景:程序A会使用1.0版本的动态链接库X,则在程序A安装到系统时,会同时安装该1.0版本的动态链接库X。假设另一个程序B也会使用到动态链接库X,那么程序B直接复制到硬盘中即可正常运行,因为动态链接库已经存在于系统中。然而有一天,另一程序C也要使用动态链接库X,但是由于程序C开发的时间较晚,其需要较新版本---2.0版本的动态链接库X。则在程序C被安装到系统时,2.0版本的动态链接库X 也必须随之安装到系统中,此时系统中1.0版本的动态链接库将被2.0版本所取代(替换)。
情况1:新版本的动态链接库不兼容旧版本。如,A何B需要X所提供的功能,在升级到2.0后,新版本的X竟然把此功能取消了(很难想象吧,呵呵但有时候就是如此....)。则此时虽然C能正常运行,但A和B均无法工作了。
情况2:新版本的动态链接库兼容旧版本,但是存在一个bug。
可看下面的例子(仅仅为了说明问题):
[cpp] view
plain copy
print?
// X1.0 version
void func(int count)
{
if(count < 0)
count = 0;
....
}
// X2.0 version
void func(int count)
{
//负数处理被移除!
...
}
一旦出现count为负数的情况,则程序A在新版本的处理下就会有问题。
解决办法:Side-by-side Assembly,是windows Xp以及以上系统解决动态链接库版本冲突所使用的技术,重点在于编译程序时,由VS生成一个manifest文件,指明当前应用程序所使用的动态链接库版本号;发布程序时需同时发布该manifest文件,供客户计算机上的DLL
Loader根据manifest加载适当版本的DLL,若不发布该项manifest,客户机则按默认版本加载DLL。下图为其典型的场景:
四、DLL与lib的关系
咋一看:lib是静态链接库;DLL是动态链接库,一个编译时提供;一个运行时提供,完了。其实没那么简单! lib也有静态lib和动态lib之分。
静态lib:它将导出声明(后面会讲)和实现均放到lib中,编译后所有代码都嵌入到宿主程序中去。
动态lib:相当于一个h文件,它是对实现部分(.DLL)的导出部分的声明。编译后只是将导出声明部分编译到宿主程序中,运行时需要相应的DLL文件的支持,否则无法工作。当生成一个新的DLL时,也会有配套的lib产生(即二者需一起分发),此时的lib即为动态lib(后面会有还有实验)。
五、如何生成一个DLL
在VC++6.0开发环境下,打开File\New\Project选项,可以选择Win32 Dynamic-Link Library或MFC AppWizard【dll】来以不同的方式创建Non-MFC DLL、Regular DLL、Extension DLL等不同种类的动态链接库。下面以选择Win32 Dynamic-Link Library方式来创建一个DLL(实现加法运算):1、创建一个Win32 Dynamic-Link Library方式的空工程,取名为myDLL
2、分别添加头文件(.h)和源文件(.cpp)
[cpp] viewplain copy
print?
// mydll.h file
extern "C" _declspec(dllexport) int add(int a, int b);
//mydll.cpp file
#include "mydll.h"
int add(int a, int b) //该DLL需要导出的函数功能:加法
{
return a + b;
}
说明:
(1)前面的 extern “C” 告诉编译器函数可以在本模块或其他模块中使用,其中“C”表明需按照C语言方式编译和连接它,因为C++编译时,会对函数名进行修饰,用于实现函数重载,而C里面没有这个功能,所以需要用extern "C"在头文件进行声明的时候加以区分,以便链接时能进行正确地函数名查找。
(2)_declspec(dllexport)为导出函数关键字,意为需从DLL中导出该函数,以便使用。
3、编译连接
在进行编译连接后会在Debug目录下找到DLL文件和对应的lib文件六、如何调用一个DLL
下面实现两种调用方式:单独.dll 和.h + .lib + .dll结合注:需把对应的 .dll 文件以及.lib 文件和.h文件(结合方式时)拷贝至调用的程序目录下
(1)单纯使用.dll
[cpp] viewplain copy
print?
#include<wtypes.h>
#include <winbase.h>
#include <iostream>
_declspec(dllimport) int Add(int a, int b); //导入声明,亦可以不加,如果加上可加快程序运行
typedef int(*pAdd)(int a,int b);
int main()
{
HINSTANCE hDLL;
pAdd Add;
hDLL=LoadLibrary("mydll.dll"); //加载 DLL文件
if(hDLL == NULL)std::cout<<"Error!!!\n";
Add=(pAdd)GetProcAddress(hDLL,"add"); //取DLL中的函数地址,以备调用
int a =Add(5,8);
std::cout<<"a: "<<a<<std::endl;
FreeLibrary(hDLL);
return 0;
}
输出结果:
(2).h + .lib + .dll 结合方式
[cpp] viewplain copy
print?
#include<wtypes.h>
#include <winbase.h>
#include <iostream>
#include "mydll.h"
#pragma comment(lib,"mydll.lib") //将mydll.lib库文件连接到目标文件中(即本工程)
extern "C"_declspec(dllimport) int add(int a,int b);
int main()
{
int a =add(5,8);
std::cout<<"a: "<<a<<std::endl;
return 0;
}
输出:
反例演示:此时如果去掉 .dll 文件(即只有.lib 和 .h文件),则会出错:
相关文章推荐
- 20145321 实验五实验报告
- CUDA学习笔记三
- 搜狗补刀推出搜狗明医
- 搜狗补刀推出搜狗明医
- Toy Program 基础线程入门 via “extends Thread”
- Jmeter之csv、用户自定义变量以及Query Type分析(八)
- CodeForces 670C Cinema
- 对于已经存在的项目,如何在不改变原来目录结构的情况下,使用maven
- Agent proxy
- hdu-2066 一个人的旅行(最短路spfa)
- java第十周学习总结
- 隐马尔可夫模型学习笔记
- Adnroid多媒体---图片
- JS学习10(DOM扩展)
- 详解Android中Drawable方法
- CodeForces 670B Game of Robots
- Android之Volley框架加载网络图片的三种方式
- 20145232 韩文浩 实验五
- <s:property>的用法
- Activity 的四种加载模式