引 言
相信有些计算机常识的伴侣都应该风闻过“DLL”。尤其是那些使用过windows操纵系统的人,都应该有过多次重装系统的“悲惨”经历——无论再怎样小心,没有驱动损坏,没有病毒侵扰,仍然在使用(安置)了一段时间软件后,发明windows系统越来越复杂,操纵越来越慢,还时时的呈现曾经能使用的软件无法使用的情况,导致最终不得不重装系统。这种情况每每是由于dll文件的大量安置和斗嘴造成的。这一方面说明DLL的不敷,另一方面也说明DLL的重要职位地方,以至我们无法杜绝它的使用。
DLL(动态链接库,Dynamic Link Library)简单来说是一种可通过挪用执行的已编译的代码模块。DLL是windows系统的早期产物。其时的主要目的是为了减少应用措施对内存的使用。只有当某个函数或过程需要被使用时,才从硬盘挪用它进入内存,一旦没有措施再挪用该DLL了,才将其从内存中断根。光说整个windows系统,就包孕了成百上千个dll文件,有些dll文件的成果是对照专业(好比网络、数据库驱动)甚至可以不安置的。假如这些成果全部要包孕在一个应用措施(Application program)里,windows将是一个数百M巨细的exe文件。这个简单的例子很容易解释DLL的感化,而挪用DLL带来的性能损掉则变得可被忽略不计。
多个应用措施挪用同一个DLL,在内存里只有一个代码副本。而不会象静态编译的措施那样每一个都必需全部的被装入。装载DLL时,它将被映射到进程的地点空间,同时使用DLL的动态链接并非将库代码拷贝,而仅仅记录函数的入口点和接口。
同时DLL还能带来的共享的好处。一家公司开发的差别软件可能需要一些公用的函数/过程,这些函数/过程可能是直接的使用一些内部开发的DLL;一些常用的成果则可以直接使用windows的标准DLL,我们常说的windows API就是包罗在windows几个公用DLL文件里的函数/过程;理论上(如果不牵涉作者的版权),知道一个DLL的声明及感化(函数界说的输入参数及返回值),我们完全可以在不清楚其实现(算法或编译方法)的情况下直接使用它。
假如一个DLL中函数/过程的算法得到了更新,BUG得到了修正,整个dll文件会得到升级。一般来说为了保证向下兼容,挪用声明与返回功效应该连结不乱。但实际上,即使是同一家开发的DLL,跟着成果的改进,也很难保证某个挪用执行完全不乱。在使用其他人开发的DLL时这种糟糕情况越发的严重。好比我在一个绘图措施里使用了某著名图形软件商旧版本的DLL包,我所有的挪用都是按照他颁布的旧版的声明来执行的。假设用户安置了该软件商的一个新软件,导致此中部分DLL被更新升级,假如这些DLL已经有过窜改,直接后果将是我的软件不再不变甚至无法运行!不要不放在眼里这种情况,事实上它是很遍及的,好比windows在修正BUG和升级过程中,就不停窜改它包罗的那些DLL。往往新版DLL不是简单的增加新的函数/过程,而是改换甚至打消了原有的声明,这时候我们再也无法保证所有措施都运行正常。
DLL除了上面提到的改进计算机资源操作率、增加开发效率、隐藏实现细节外,还可以包罗数据和各类资源。好比开发一个软件的多国语言版,就可以使用DLL将依赖于语言的函数和资源疏散出来,然后让各地的用户安置差别对应的DLL,以获取本地字符集的撑持。再好比一个软件必需的图形、图标等资源,也可以直接放在dll文件中统一安置打点。 创建一个DLL
在进行后面的讲解之前,我想大家应该先清楚一个观点:例程声明的是一个指针变量,挪用函数/过程,其实是通过指针转入该函数/过程的执行代码。
我们先测验考试用Delphi来成立一个本身的DLL文件。这个DLL包罗一个标准的目录删除(包罗子目录及文件)函数。
成立DLL
通过Delphi成立一个DLL是很容易的。New一个新Project,选择DLL Wizard,然后会生成一个非常简单的单元。该单元不象一般的工程文件以program开始,而是以library开始的。
该工程单元缺省引用了SysUtils、Classes两个单元。可以直接在该单元的uses之后,begin … end部分之前添加函数/过程代码,也可以在工程中添加包罗代码的单元,然后该单元将会被自动uses。
接下来是编写DLL例程的代码。如果是引用单元里的例程,需要通过声明时添加export后缀引出。假如是直接写在library单元中的,则不必再写export了。
最后一步是在library单元的begin语句之上,uses部分及函数界说之下添加exports部分,并列举需要引出的例程名称。注意仅仅是名称,不包罗procedure或function关键字,也不需要参数、返回值和后缀。
exports语句后的语法有三种形式(例程指具体的函数/过程):
exports例程名;
exports例程名 index 索引值;
exports例程名 name新名称;
索引值和新名称便于其他措施确定函数地点;也可以不指定,如果没有使用Index关键字,Delphi将凭据exports后的挨次从1开始自动分配索引号。Exports后可跟多个例程,之间以逗号分隔断绝分手。
编译,build最终的dll文件。
需注意的格局
为了保证生成的DLL能正确与C++等语言兼容,需要注意以下几点:
尽量使用简单类型或指针作为参数及返回值的类型。这里的简单类型是指C++的简单类型,所以string字符串类型最好转换成Pchar字符指针。直接使用string的DLL例程在Delphi开发的措施中挪用是没有问题的(有资料指出需插手ShareMem做为第一单元以确保正确),但如果使用C++或其他语言开发的措施挪用,则不能保证参数通报正确;
虽然过程是允许的,但是最好习惯全部写成函数。过程则返回执行正确与否的true/false;
对付参数的指示字好比const(只读)、out(只写)等等,为保证挪用的兼容性,最好使用缺省方法(缺省var,即可读写的地点);
使用stdcall声明后缀,以保证正确的异常措置惩罚惩罚。16位DLL无法通过这种方法措置惩罚惩罚异常,所以还得在例程最外层用Try … Except将异常措置惩罚惩罚失;
一般不使用far后缀,除非为了连结与16位兼容。
典型代码
DLL工程单元: