第一章 DLL简单介绍
由于在目前的学习工作中,需要用到DLL文件,就学习了下,在这里作个总结。
首先装简单介绍下DLL:
1,减小可执行文件的大小
DLL技术的产生有很大一部分原因是为了减小可执行文件的大小。当操作系统进入Windows时代后,其大小已经达到几十兆乃至几百兆。试想如果还是使用DOS时代的单执行文件体系的话一个可执行文件的大小可能将达到数十兆,这是大家都不能接受的。解决的方法就是采用动态链接技术将一个大的可执行文件分割成许多小的可执行程序。
2,实现资源共享
这里指的资源共享包括很多方面,最多的是内存共享、代码共享等等。DLL还有一个突出的特点就是在内存中只装载一次,这一点可以节省有限的内存,而且可以同时为多个进程服务。
3,便于维护和升级
在软件运行出现问题的时候,我们并不需要重新安装程序,只需要替换相应的DLL文件即可。
4,比较安全
这里说的安全也包括很多方面。比如,DLL文件遭受病毒的侵害机率要比普通的EXE文件低很多。另外,由于是动态链接的,这给一些从事破坏工作的“高手”们多少带来了一些反汇编的困难。
第二章 在Delphi中编写DLL文件
编写DLL文件其实不是什么困难的事情,和我们平时在Delphi中编写程序基本相似,下面先以一个简单的例子来说明。
- library DLL;
- { Important note about DLL memory management: ShareMem must be the
- first unit in your library's USES clause AND your project's (select
- Project-View Source) USES clause if your DLL exports any procedures or
- functions that pass strings as parameters or function results. This
- applies to all strings passed to and from your DLL--even those that
- are nested in records and classes. ShareMem is the interface unit to
- the BORLNDMM.DLL shared memory manager, which must be deployed along
- with your DLL. To avoid using BORLNDMM.DLL, pass string information
- using PChar or ShortString parameters. }
- uses
- ShareMem,
- SysUtils,
- Classes;
- function testStr: TStringList; stdcall;
- var
- str: string;
- strlist: TStringList;
- begin
- strlist := TStringList.Create;
- strlist.Add('hello');
- strlist.Add('world');
- str := 'hello world';
- result := strlist;
- end;
- function testInt(i: Integer): Integer; stdcall;
- begin
- Inc(i);
- result := i;
- end;
- {$R *.res}
- exports
- testStr,
- testInt;
- begin
- end.
可以看出在上面的Dll文件中我们封装了两个函数。跟一般Delphi编写的程序基本相同,只是在DLL文件中的函数后面多了一个stdcall参数,并且用exports声明需要引出的函数。只要编译上述代码就可以得到一个动态链接库的Dll文件。
1,在Dll中编写的函数必须在后面加上stdcall调用参数,如果忘记添加stdcall参数的话虽然编译可以通过,但在调用这个DLL时会出现很严重的错误,导致操作系统的死锁。
2,所写的函数和过程必须用exports声明为外部函数。若没有声明,那Dll内部的函数是不可调用的,这一点显然不是我们想看到的。
3,当使用了长字符串类型的参数、变量时,如string,要引用ShareMem。虽然Delphi中的string功能很强大,但若是您编写的Dll文件要供其它编程语言调用时,最好使用PChar类型。如果您要坚持使用string类型的参数时、变量甚至是记录信息时,就要引用ShareMem单元,而且这个单元必须是第一个引用的,即在uses语句后的第一个单元。如下:
- uses
- ShareMem,
- SysUtils,
- Classes;
另外,还有一个地方我们需要添加ShareMem单元,即在您的工程文件中(*.dpr)中的uses下第一个引用ShareMem单元。这里需要注意的是在*.dpr文件中而不是单元文件(*.pas)中添加相同的单元,这一点Delphi自带的帮助文件没有说清楚,造成了许多误会。若不这样做的话,您可能付出死机的代价。避免使用string类型的方法是将其转换为PChar或ShortString类型。
第三章 Dll文件的调用
一,静态调用
调用Dll文件要比编写Dll容易多了,这里先总结一下Dll的静态调用。首先看下面的小例子:
- function testInt(i: Integer): Integer; stdcall; external 'DLL.dll';
- procedure TForm1.btn2Click(Sender: TObject);
- begin
- showMessage(inttostr(testInt(1)));
- end;
大家可以看到我们做的唯一的工作是将Dll中的函数说明放在implementation下(或是放在文件单元的var下),并且用external语句指定了Dlll的位置。这样我们就可以直接使用Dll中的函数了。
1,调用参数stdcall,在引用Dll中的函数时也要使用stdcalll参数。
2,用external声明Dll文件的位置。注意文件的后缀名必须加上。
3,不能从Dll中调用全局变量。如果我们在DLL中声明了某种全局变量,如:var s:byte 。这样在DLL中s这个全局变量是可以正常使用的,但s不能被调用程序使用,既s不能作为全局变量传递给调用程序。不过在调用程序中声明的变量可以作为参数传递给DLL。
二,动态调用
动态调用Dll文件要复杂很多,但非常灵活。在运行大的程序时可以节省内存空间。我们看下面的例子:
- procedure TForm1.btn1Click(Sender: TObject);
- type
- Taddc = function: TStringList; stdcall;
- var
- hh: THandle;
- addc: Taddc;
- temp: TStringList;
- i: Integer;
- begin
- hh := LoadLibrary('DLL.dll');
- try
- if hh <> 0 then
- @addc := GetProcAddress(hh, PChar('testStr'));
- if not (@addc = nil) then
- begin
- temp := addc;
- for i := 0 to temp.Count - 1 do
- showMessage(temp[i]);
- end
- else
- begin
- RaiseLastWin32Error;
- end;
- finally
- FreeLibrary(hh);
- end;
- end;
由上面代码可以看到,这种动态调用技术很复杂。但只要更改参数就可动态更改所调用Dll文件的名字。
1,在type中定义所要调用的函数或过程的类型,在这里同样也要加上stdcall参数。
2,释放Dll。在上面程序中我们使用LoadLibray函数加载了dll文件,在使用完Dll文件时切记调用FreeLibrary函数来释放Dll,否则Dll将会一直占用你的内存直至您退出Windows或是关机为止。在这里需要注意的是要确保在释放dll的时候没有任何指针指向该dll中所定义的变量、过程等,否则无法正常释放,招聘访问冲突异常。或是导致程序出现空指针,导致程序意外中止。
3,在动态调用的Dll函数中我们使用了string类型,由于在两处文件内都引用了ShareMem,所以string类型的数据可以正使用,以及作为参数来传递。
三,静态和动态调用的对比
1,静态方法实现简单,易于掌握。并且一般来说速度比较快,也更加安全可靠一些。但静态方法不能灵活地在运行时装卸所需的Dll,而主程序在开始运行时就装载了Dll只到程序结束时才释放该Dll。
2,动态方法很好地解决了静态方法中的不足,可以方便的访问Dll中的函数和过程。但动态方法难以完全掌握,使用时因为不同的函数或过程需要定义很多复杂的类型和调用方法。