Windows动态链接库DLL使用

  • 格式:pdf
  • 大小:140.12 KB
  • 文档页数:3

Windows动态链接库DLL使⽤

⼀、概念

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。

可看下⾯的例⼦(仅仅为了说明问题):

// X1.0 version

void func(int count)

{

if(count < 0)

count = 0;

....

}

// X2.0 version

void func(int count)

{

//负数处理被移除!

...

}

⼀旦出现count为负数的情况,则程序A在新版本的处理下就会有问题。

解决办法:,是windows Xp以及以上系统解决动态链接库版本冲突所使⽤的技术,重点在于编译程序时,由VS⽣成⼀个manifest⽂件,指

明当前应⽤程序所使⽤的动态链接库版本号;发布程序时需同时发布该manifest⽂件,供客户计算机上的DLL Loader根据manifest加载适当

版本的DLL,若不发布该项manifest,客户机则按默认版本加载DLL。下图为其典型的场景:四、DLL与lib的关系

咋⼀看:lib是静态链接库;DLL是动态链接库,⼀个编译时提供;⼀个运⾏时提供,完了。

其实没那么简单!。

静态lib:它将导出声明(后⾯会讲)和实现均放到lib中,编译后所有代码都嵌⼊到宿主程序中去。

动态lib:相当于⼀个h⽂件,它是对实现部分(.DLL)的导出部分的声明。编译后只是将导出声明部分编译到宿主程序中,运⾏时需要相应

的DLL⽂件的⽀持,否则⽆法⼯作。当⽣成⼀个新的DLL时,也会有配套的lib产⽣(即⼆者需⼀起分发),此时的lib即为动态lib(后⾯会有

还有实验)。

五、如何⽣成⼀个DLL

在VS2012开发环境下,打开File\New\Project选项,可以选择Win32 Dynamic-Link Library或MFC AppWizard【dll】来以不同的⽅式创建

Non-MFC DLL、Regular DLL、Extension DLL等不同种类的动态链接库。下⾯以选择Win32 Dynamic-Link Library⽅式来创建⼀个DLL(实

现加法运算)

2、分别添加头⽂件(.h)和源⽂件(.cpp)

// 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)为导出函数关键字,意为需从DLL中导出该函数,以便使⽤

3、编译连接

在进⾏编译连接后会在Debug⽬录下找到DLL⽂件和对应的lib⽂件

六、如何调⽤⼀个DLL

下⾯实现两种调⽤⽅式:单独.dll 和.h + .lib + .dll结合

注:需把对应的 .dll ⽂件以及.lib ⽂件和.h⽂件(结合⽅式时)拷贝⾄调⽤的程序⽬录下

(1)单纯使⽤.dll

#include

#include

#include

_declspec(dllimport) int Add(int a, int b); //导⼊声明,亦可以不加,如果加上可加快程序运⾏

typedef int(*pAdd)(int a,int b);

int main()

{

HINSTANCE hDLL;

pAdd Add;

hDLL=LoadLibrary(L"mydll.dll"); //加载 DLL⽂件

if(hDLL == NULL)std::cout<<"Error\n";

Add=(pAdd)GetProcAddress(hDLL,"add"); //取DLL中的函数地址,以备调⽤

int a =Add(5,8);

std::cout<<"a: "<

FreeLibrary(hDLL);

return 0;

}

输出结果:

(2).h + .lib + .dll 结合⽅式

#include

#include

#include #include "../MyDll/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: "<

return 0;

}

此时如果去掉 .dll ⽂件(即只有.lib 和 .h⽂件),则会出错: