Windows的动态链接库原理
动态链接库(DLLs)是从C语言函数库和Pascal库单元的概念发展而来的所有的C语言标准库函数都存放在某一函数库中同时用户也可以用LIB程序创建自己的函数库在链接应用程序的过程中链接器从库文件中拷贝程序调用的函数代码并把这些函数代码添加到可执行文件中这种方法同只把函数储存在已编译的OBJ文件中相比更有利于代码的重用
但随着Windows这样的多任务环境的出现函数库的方法显得过于累赘如果为了完成屏幕输出消息处理内存管理对话框等操作每个程序都不得不拥有自己的函数那么Windows程序将变得非常庞大Windows的发展要求允许同时运行的几个程序共享一组函数的单一拷贝动态链接库就是在这种情况下出现的动态链接库不用重复编译或链接一旦装入内存Dlls函数可以被系统中的任何正在运行的应用程序软件所使用而不必再将DLLs函数的另一拷贝装入内存
动态链接库的工作原理
动态链接这几字指明了DLLs是如何工作的对于常规的函数库链接器从中拷贝它需要的所有库函数并把确切的函数地址传送给调用这些函数的程序而对于DLLs函数储存在一个独立的动态链接库文件中在创建Windows程序时链接过程并不把DLLs文件链接到程序上直到程序运行并调用一个DLLs中的函数时该程序才要求这个函数的地址此时Windows才在DLLs中寻找被调用函数并把它的地址传送给调用程序采用这种方法DLLs达到了复用代码的极限
动态链接库的另一个方便之处是对动态链接库中函数的修改可以自动传播到所有调用它的程序中而不必对程序作任何改动或处理
DLLs不仅提供了函数重用的机制而且提供了数据共享的机制任何应用程序都可以共享由装入内存的DLLs管理的内存资源块只包含共享数据的DLLs称为资源文件如Windows的字体文件等
Windows系统的动态链接库
Windows本身就是由大量的动态链接库支持的这包括Windows API函数 ( KRNLxEXEUSEREXEGDIEXE…)各种驱动程序文件各种带有Fon和Fot 扩展名的字体资源文件等Windows还提供了针对某一功能的专用DLLs如进行DDE编程的ddemldll进行程序安装的verdll等
虽然在编写Windows程序时必然要涉及到DLLs但利用Delphi 用户在大部分时候并不会注意到这一点这一方面是因为Delphi提供了丰富的函数使用户不必直接去使用Windows API;另一方面即使使用Windows API由于Delphi把API函数和其它Windows DLLs函数重新组织到了几个库单元中因而也不必使用特殊的调用格式所以本章的重点放在编写和调用用户自定义的DLLs上
使用传统的Windows编程方法来创建和使用一个DLLs是一件很令人头痛的事正如传统的Windows编程方法本身就令人生畏一样用户需要对定义文件工程文件进行一系列的修改以适应创建和使用DLLs的需要Delphi的出现在这一方面正如在其它许多方面所做的那样减轻了开发者的负担更令人兴奋的是Delphi利用DLLs 实现了窗体的重用机制用户可以将自己设计好的窗体储存在一个DLLs中在需要的时候可随时调用它
DLLs的编写和调用
DLLs的编写
在Delphi环境中编写一个DLLs同编写一个一般的应用程序并没有太大的区别事实上作为DLLs 主体的DLL函数的编写除了在内存资源的管理上有所不同外并不需要其它特别的手段真正的区别在工程文件上
在绝大多数情况下用户几乎意识不到工程文件的存在因为它一般不显示在屏幕上如果想查看工程文件则可以打开View菜单选择Project Source项此时工程文件的代码就会出现在屏幕的Code Editor(代码编辑器)中
一般工程文件的格式为
program 工程标题
uses 子句
程序体
而DLLs工程文件的格式为
library 工程标题
uses 子句
exprots 子句
程序体
它们主要的区别有两点
一般工程文件的头标用program关键字而DLLs工程文件头标用library 关键字不同的关键字通知编译器生成不同的可执行文件用program关键字生成的是exe文件而用library关键字生成的是dll文件
假如DLLs要输出供其它应用程序使用的函数或过程则必须将这些函数或过程列在exports子句中而这些函数或过程本身必须用export编译指令进行编译
根据DLLs完成的功能我们把DLLs分为如下的三类
完成一般功能的DLLs;
用于数据交换的DLLs;
用于窗体重用的DLLs
这一节我们只讨论完成一般功能的DLLs其它内容将在后边的两节中讨论
编写一般DLLs的步骤
编写一般DLLs的步骤如下
利用Delphi的应用程序模板建立一个DLLs程序框架
对于Delphi 的用户由于没有DLLs模板因此
()建立一个一般的应用程序并打开工程文件
()移去窗体和相应的代码单元
()在工程文件中把program改成library移去Uses子句中的Forms并添加适当的库单元(一般SysUtilsClasses是需要的)删去begin…end之间的所有代码
以适当的文件名保持文件此时library后跟的库名自动修改
输入过程函数代码如果过程函数准备供其它应用程序调用则在过程函数头后加上export 编译指示
建立exports子句包含供其它应用程序调用的函数和过程名可以利用标准指示 name Indexresident以方便和加速过程/函数的调用
输入库初始化代码这一步是可选的
编译程序生成动态链接库文件
动态链接库中的标准指示
在动态链接库的输出部分用到了三个标准指示nameIndexresident
name
name后面接一个字符串常量作为该过程或函数的输出名如
exports
InStr name MyInstr;
其它应用程序将用新名字(MyInstr)调用该过程或函数如果仍利用原来的名字(InStr)则在程序执行到引用点时会引发一个系统错误
Index
Index指示为过程或函数分配一个顺序号如果不使用Index指示则由编译器按顺序进行分配
Index后所接数字的范围为…使用Index可以加速调用过程
resident
使用resident则当DLLs装入时特定的输出信息始终保持在内存中这样当其它应用程序调用该过程时可以比利用名字扫描DLL入口降低时间开销
对于那些其它应用程序常常要调用的过程或函数使用resident指示是合适的例如
exports
InStr name MyInStr resident;
[] [] [] []