第22章 动态链接库编程(www.cppentry.com)
在应用程序开发的过程中,有的功能是在很多地方都可以重复使用,如有关数据的访问、协议转换等。为了提高这些功能的复用,减少系统开发工作量,系统提供了动态链接库(Dynamic-link libraries,DLL)技术,实现功能模块化。本章将讲述有关动态链接库的编程(www.cppentry.com)。
22.1 基本概念
动态链接库是包含函数和数据的模块,将实现一定功能的函数和数据按照一定的规则封装在一起。本节将介绍有关动态链接库的基本概念及工作方式。
22.1.1 动态链接库的概念
简单地讲,DLL就是完成一定功能的模块,既可以包含数据、函数,也可以包含类。DLL最典型的例子--微软的Win32应用程序接口,就是通过一组动态链接库的方式实现的。因此,任何程序都可以通过调用动态库的方式使用Win32 API,从而可以访问系统底层接口。DLL中包含两种对象。
导出对象:如导出数据、导出函数和导出类,此种对象可以被其他可执行模块调用。虽然DLL可以导出数据,但是通常DLL中的数据都是内部数据,仅供内部函数使用,不建议从DLL中导出数据。
内部对象:如内部数据、内部函数和内部类,此种对象只能在DLL内部由内部使用。
DLL动态链接库和EXE可执行文件类似,都是可执行程序模块,但是也存在很多不同之处。对于用户来说,最大的差别在于DLL不是可以直接执行的程序。从系统的角度来看,它们之间存在两个基本的区别。一是应用程序可以在系统中同步运行多个实例,而DLL只能有一个实例。二是应用程序可以管理诸如堆栈、全局内存、文件句柄和消息队列等资源,而DLL不能管理这些资源。DLL的工作过程如图22-1所示。
在图22-1中列出了DLL被调用的工作过程。假定有3个DLL,分别是A.DLL、B.DLL、C.DLL,有两个程序,分别是程序1、程序2。程序1需要调用A.DLL和B.DLL,而程序2需要调用B.DLL和C.DLL。
从图22-1可以看出,DLL由调用模块在运行时进行装载,调用模块装载DLL后,将DLL映射到虚拟地址空间中,这样可以减少同一时间多个应用程序使用相同功能所重复使用的内存。因为虽然每个应用程序有自己的数据备份,但是共享相同的代码。这也是动态链接与静态链接的区别,对于静态链接,链接器需要复制函数代码到调用模块的数据空间中。
|
| (点击查看大图)图22-1 DLL工作过程 |
同时,动态链接允许模块在装载时,仅包含系统需要的信息,或者运行时定位到导出DLL函数的代码。
对于被调用的DLL来说,系统会维护被引用的次数,每有一个进程调用DLL,则DLL的引用计数会增加一,每有一个引用DLL的进程终止,DLL的引用计数会减一。直到DLL的引用计数变成0,DLL会被系统卸载,退出地址空间。
但是需要注意的是,虽然调用DLL的进程都使用相同的DLL副本,但是调用DLL的进程所使用的DLL导出的函数是运行在调用进程或线程的上下文的,因此DLL在处理资源时,是这样处理的。
调用DLL的进程的线程可以使用DLL函数打开的句柄。同样,DLL函数也可以使用调用DLL的进程的线程打开的句柄。
DLL可以使用调用线程的堆栈和进程的虚拟地址空间。
DLL从调用进程的虚拟地址空间中分配内存。
总之,调用DLL是多个模块共享相同的DLL代码,但是每个模块使用的DLL又是工作在模块自己的上下文环境中的。