Windows的动态链接库原理_第1页
Windows的动态链接库原理_第2页
Windows的动态链接库原理_第3页
Windows的动态链接库原理_第4页
Windows的动态链接库原理_第5页
已阅读5页,还剩6页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、包含的目录。函数GetWindowDirectory返回这一目录的路径; (3Windows系统目录(包含系统文件如gdi.exe的目录。函数GetSystemDirectory返回这一目录的路径; (4包含当前任务可执行文件的目录。利用函数GetModuleFileName可以返回这一目录的路径; (5列在PATH环境变量中的目录; (6网络的映象目录列表。 如果函数执行成功,则返回装载库模块的实例句柄。否则,返回一个小于HINSTANCE_ERROR的错误代码。错误代码的意义如下表: 表10.2 Loadlibrary返回错误代码的意义 错误代码意义 0 系统内存不够,可执行文件被破坏或调

2、用非法 2 文件没有被发现 3 路径没有被发现 5 企图动态链接一个任务或者有一个共享或网络保护错 6 库需要为每个任务建立分离的数据段 8 没有足够的内存启动应用程序 10 Windows版本不正确 11 可执行文件非法。或者不是Windows应用程序,或者在.EXE映 像中有错误 12 应用程序为一个不同的操作系统设计(如OS/2程序 13 应用程序为MS DOS4.0设计 14 可执行文件的类型不知道 15 试图装载一个实模式应用程序(为早期Windows版本设计 16 试图装载包含可写的多个数据段的可执行文件的第二个实例 19 试图装载一个压缩的可执行文件。文件必须被解压后才能被装裁

3、20 动态链接库文件非法 21 应用程序需要32位扩展 假如在应用程序用Loadlibrary调用某一模块前,其它应用程序已把该模块装入内存,则Loadlibrary并不会装载该模块的另一实例,而是使该模块的“引用计数”加1。 2.GetProcAddress:捡取给定模块中函数的地址 语法为: function GetProcAddress(Module: THandle; ProcName: PChar: TFarProc; Module包含被调用的函数库模块的句柄,这个值由Loadlibrary返回。如果把Module设置为nil,则表示要引用当前模块。 ProcName是指向含有函数名

4、的以nil结尾的字符串的指针,或者也可以是函数的次序值。如果ProcName参数是次序值,则如果该次序值的函数在模块中并不存在时,GetProcAddress仍返回一个非nil的值。这将引起混乱。因此大部分情况下用函数名是一种更好的选择。如果用函数名,则函数名的拼写必须与动态链接库文件E ,将会使你的应用增色不少。 DLL 中的类与普通类没有什么差别,对于DLL 的处理,只要记住两个要点: 1、不能在一端分配内存,而在另一端释放; 2、必须有类的数据导出处理,接口中处理的应是标准数据类型,想在导出函数中直接处理类只会增加你的麻烦。 注:可以利用接口处理导出类,但那对 OLE 或许还有用,对于一

5、般的应用,简而避之。 下面是一个例子: DLL: type TMyDLLClass = class private FData: string; public constructor Create; destructor Destroy; override; property Data: string read FData write FData; end; 定义几个通用的接口: type TMyDLLOBJ = type Pointer; /定义 DLL 中的通用对象指针 function CreateMyDLLObj: TMyDLLOBJ; stdcall; /初始化类 function

6、DoneMyDLLObj(OBJ: TMyDLLOBJ; stdcall; /释放类 然后增加你的导出函数: procedure FetchMyDLLData(OBJ: TMyDLLOBJ; Buffer: PChar; Size: Longint: stdcall; var MyDLLClass: TMyDLLClass; begin MyDLLClass := TMyDLLClass(OBJ; if MyDLLClass <> nil then StrPLCopy(Buffer, MyDLLClass.Data, Size; end; 看出来了吗,就这么简单,而且相当可靠。我的

7、应用大多采用这种处理方式,在 DLL 中处理数据库,用一个数据记录来传递和处理信息。应用程序完全与数据库分开,要更换数据库类型,只要更新 DLL,应用程序根本不必改动!而且不管用什么语言作主应用,都可以利用 Delphi 的强大部分(单元结构 - 让你的代码容易管理;TList - 我用得是最多的一个类,加载数据表;以及 string ,可以使你在处理串时偷到不少懒.)来使你的应用更加容易开发! 2003-10-31 14:57:37 使用动态链接库简介:本章内容: . 究竟什么是D L L . 静态链接与动态链接 . 为什么使用D L L . 创建和使用D L L . 显示D L L中的模式

8、窗体 . 在D e l p h i应用程序中使用D L L . DLL的入口和出口函数 . DLL中的异常 . 回调函数 . 从D L L中调用回调函数 . 在不同的过程中共享D L L数据 . 引出D L L中的对象 本章讨论了Wi n 3 2动态链接库,也就是D L L。D L L是用来编写Wi n d o w s应用程序的关键组成部分。 本章讨论了使用和创建D L L的几个方面,它给出了D L L怎样工作的概述并讨论了怎样创建和使用D L L, 你将学会怎样调入D L L和链接由它们引出的过程和函数的不同方法。本章还包括回调函数的使用并举例说明在不同调用进程中如何实现共享数据。 1 究竟

9、什么是DLL 动态链接库是程序模块,它包括代码、数据或资源,能够被其他的Wi n d o w s应用程序共享。DLL的主要特点之一是应用程序可以在运行时调入代码执行,而不是在编译时链接代码,因此,多个应用程序可以共享同一个D L L的代码。事实上,文件Kernel32.dll、User32.dll、GDI32.dll就 函数链接到可执行文件中。其中的引用可以在应用程序中声明,但是通常情况下是放在一个专门的引入(import单后面的字符串就是该DLL的名称。要使用这个单元,应用程序只需把MaxUnit加到它的uses子句中即可。 当这个程序运行时,该DLL就会自动地被调入内存,并且任何需要调用M

10、ax(的程序都被链接到这个DLL中的Max(函数。 调用DLL有两种方式,这是其中一种,叫隐式调用,就是让Windows在应用程序调入时自动地调 3 为什么要使用DLL 使用D L L有若干理由,其中有一些前面已经提到过了。大体说来,使用动态链接库可以共享代码、系统资源,可以隐藏实现的代码或底层的系统例程、设计自定义控件。下面将分别讨论这几个方面的内容。 3.1 共享代码、资源和数据 在本章节前面已经提到,共享代码是创建动态链接库的主要目的所在。但与单元的代码共享不同,DLL的代码可以被任何Windows应用程序共享,而单元代码的共享局限于Delphi应用程序。 另外,DLL提供了共享资源的途

11、径,诸如位图、字体、图标等等这些都可以放到一个资源文件中,并直接链接到应用程序。如果把这些资源放到DLL中,那么就可以让许多应用程序使用,而不必在内存里重复装入这些资源。 这行代码的意思表明,ClientToSreen(在动态链接库User32.dll中,它的名称叫ClientToSreen. 在1 6位的Windows中,DLL有自己的数据段,于是,所有要调用同一个DLL的应用程序能够访问同一个全局变量和静态变量。但在Win32系统中,这就不同了。因为DLL的映像被映射到每个进程的地址空间,该DLL的所有数据属于映射到的进程。值得一说的是,尽管进程间不能共享DLL的数据,但是同一个进程的所有

12、线程可以共享,因为线程是相互独立的,所以在访问某一DLL的全局变量时,务必小心,防止引起冲突。 但这并不意味着没有办法实现在进程间共享一个DLL的数据。一个技术可以通过内存映射文件的方法在内存中创建一个共享的区域。一切需调用D L L的应用程序都可以读这些存储在内存中的共享区域的数据。 3.2 隐藏实现的细节 有些时候,你可能想隐藏例程实现的细节,DLL就可以实现这一点。不管为何要隐藏你的代码,DLL可以使函数被应用程序访问,而其中的代码细节不被显现,你所要做的只是提供别人能访问DLL的接口。你也许认为Delphi的编译单元(DCU也可以隐藏细节,但是DCU只适用于Delphi应用程序,而且还

13、受版本的局限。而DLL与语言无关,所以,创建的DLL可以被C+、VB或其他任何支持DLL的语言调用。 Windows单元是Win32 DLL的接口单元。Delphi提供了Win32 API的源文件,其一是Windows单元的源文件windows.pas. 3.3 自定义 控件 自定义控件通常放在DLL中。这些控件不同于Delphi的自定义组件。自定义控件是在Windows下注册,并且可以在任何Windows开发环境中使用。将这些类型的自定义控件加进DLL中,是考虑到即使有多个应用程序要使用这些自定义控件,内存中也只有该控件的一份实例。 注意其实将自定义控件加进DLL这种机制已经过时,现在,微软

14、使用OLE和ActiveX控件,自定义控件已很少见了。 2003-10-31 15:13:22 Delphi环境中编写调用DLL的方法和技巧 第一章 为什么要使用动态链接库(DLL) 提起DLL您一定不会陌生,在Windows中有着大量的以DLL为后缀的文件,它们是保证Windows正常运行和维护升级的重要保证。其实,DLL是一种特殊的可执行文件。说它特殊主要是因为一般它都不能直接运行,需要宿主程序比如*.EXE程序或其他DLL的动态调用才能够使用。简单的说,在通常情况下DLL是经过编译的函数和过程的集合。 使用DLL技术主要有以下几个原因: 一、减小可执行文件大小。 DLL技术的产生有很大一

15、部分原因是为了减小可执行文件的大小。当操作系统进入Windows时代后,其大小已经达到几十兆乃至几百兆。试想如果还是使用DOS时代的单执行文件体系的话一个可执行文件的大小可能将达到数十兆,这是大家都不能接受的。解决的方法就是采用动态链接技术将一个大的可执行文件分割成许多小的可执行程序。 二、实现资源共享。 这里指的资源共享包括很多方面,最多的是内存共享、代码共享等等。早期的程序员经常碰到这样的事情,在不同的编程任务中编写同样的代码。这种方法显然浪费了很多时间,为了解决这个问题人们编写了各种各样的库。但由于编程语言和环境的不同这些库一般都不能通用,而且用户在运行程序时还需要这些库才行,极不方便。

16、DLL的出现就像制定了一个标准一样,使这些库有了统一的规范。这样一来,用不同编程语言的程序员可以方便的使用用别的编程语言编写的DLL。另外,DLL还有一个突出的特点就是在内存中只装载一次,这一点可以节省有限的内存,而且可以同时为多个进程服务。 三、便于维护和升级。 细心的朋友可能发现有一些DLL文件是有版本说明的。(查看DLL文件的属性可以看到,但不是每一个DLL文件都有)这是为了便于维护和升级。举个例子吧,早期的Win95中有一个BUG那就是在闰年不能正确显示2月29日这一天。后来,Microsoft发布了一个补丁程序纠正了这个BUG。值得一提的是,我们并没有重装Win95,而是用新版本的D

17、LL代替了旧版本的DLL。(具体是哪一个DLL文件笔者一时想不起来了。)另一个常见的例子是驱动程序的升级。例 如,著名的DirectX就多次升级,现在已经发展到了6.0版了。更妙的是,当我们试图安装较低版本的DLL时,系统会给我们提示,避免人为的操作错误。例如我们升级某硬件的驱动程序时,经常碰到Windows提示我们当前安装的驱动程序比原来的驱动程序旧。 四、比较安全。 这里说的安全也包括很多方面。比如,DLL文件遭受病毒的侵害机率要比普通的EXE文件低很多。另外,由于是动态链接的,这给一些从事破坏工作的“高手”们多少带来了一些反汇编的困难。 第二章 在Delphi中编写DLL top 说了那

18、么多,总该言归正传了。编写DLL其实也不是一件十分困难的事,只是要注意一些事项就够了。为便于说明,我们先举一个例子。 library Delphi; uses SysUtils, Classes; function TestDll(i:integer:integer;stdcall; begin Result:=i; end; exports TestDll; begin end. 上面的例子是不是很简单?熟悉Delphi的朋友可以看出以上代码和一般的Delphi程序的编写基本是相同的,只是在TestDll函数后多了一个stdcall参数并且用exports语句声明了TestDll函数。只要编

19、译上面的代码,就可以得到一个名为Delphi.dll的动态链接库。现在,让我们来看看有哪些需要注意的地方。 一、在DLL中编写的函数或过程都必须加上stdcall调用参数。在Delphi 1或Delphi 2环境下该调用参数是far。从Delphi 3以后将这个参数变为了stdcall,目的是为了使用标准的Win32参数传递技术来代替优化的register参数。忘记使用stdcall参数是常见的错误,这个错误不会影响DLL的编译和生成,但当调用这个DLL时会发生很严重的错误,导致操作系统的死锁。原因是register参数是Delphi的默认参数。 二、所写的函数和过程应该用exports语句声

20、明为外部函数。 正如大家看到的,TestDll函数被声明为一个外部函数。这样做可以使该函数在外部就能看到,具体方法是单激鼠标右键用“快速查看(QuickView)”功能查看该DLL文件。(如果没有“快速查看”选项可以从Windows CD上安装。)TestDll函数会出现在ExportTable栏中。另一个很充分的理由是,如果不这样声明,我们编写的函数将不能被调用,这是大家都不愿看到的。 三、当使用了长字符串类型的参数、变量时要引用ShareMem。 Delphi中的string类型很强大,我们知道普通的字符串长度最大为256个字符,但Delphi中string类型在默认情况下长度可以达到2G

21、。(对,您没有看错,确实是两兆。)这时,如果您坚持要使用string类型的参数、变量甚至是记录信息时,就要引用ShareMem单元,而且必须是第一个引用的。既在uses语句后是第一个引用的单元。如下例: uses ShareMem, SysUtils, Classes; 还有一点,在您的工程文件 (*.dpr)中而不是单元文件(*.pas)中也要做同样的工作,这一点Delphi自带的帮助文件没有说清楚,造成了很多误会。不这样做的话,您很有可能付出死机的代价。避免使用string类型的方法是将string类型的参数、变量等声明为Pchar或ShortString(如:s:string10)类型。

22、同样的问题会出现在当您使用了动态数组时,解决的方法同上所述。 第三章 在Delphi中静态调用DL 调用一个DLL比写一个DLL要容易一些。首先给大家介绍的是静态调用方法,稍后将介绍动态调用方法,并就两种方法做一个比较。同样的,我们先举一个静态调用的例子。 unit Unit1; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm Edit1: TEdit; Button1: TButton; pr

23、ocedure Button1Click(Sender: TObject; private Private declarations public Public declarations end; var Form1: TForm1; implementation $R *.DFM /本行以下代码为我们真正动手写的代码 function TestDll(i:integer:integer;stdcall; external Delphi.dll; procedure TForm1.Button1Click(Sender: TObject; begin Edit1.Text:=IntToStr(

24、TestDll(1; end; end. 上面的例子中我们在窗体上放置了一个编辑框(Edit)和一个按钮(Button),并且书写了很少的代码来测试我们刚刚编写的Delphi.dll。大家可以看到我们唯一做的工作是将TestDll函数的说明部分放在了implementation中,并且用external语句指定了Delphi.dll的位置。(本例中调用程序和Delphi.dll在同一个目录中。)让人兴奋的是,我们自己编写的TestDll函数很快被Delphi认出来了。您可做这样一个实验:输入“TestDll(”,很快Delphi就会用fly-by提示条提示您应该输入的参数是什么,就像我们使用D

25、elphi中定义的其他函数一样简单。注意事项有以下一些: 一、调用参数用stdcall。 和前面提到的一样,当引用DLL中的函数和过程时也要使用stdcall参数,原因和前面提到的一样。 二、用external语句指定被调用的DLL文件的路径和名称。 正如大家看到的,我们在external语句中指定了所要调用的DLL文件的名称。没有写路径是因为该DLL文件和调用它的主程序在同一目录下。如果该DLL文件在C:,则我们可将上面的引用语句写为external C:Delphi.dll。注意文件的后缀.dll必须写上。 三、不能从DLL中调用全局变量。 如果我们在DLL中声明了某种全局变量,如:var

26、 s:byte 。这样在DLL中s这个全局变量是可以正常使用的,但s不能被调用程序使用,既s不能作为全局变量传递给调用程序。不过在调用程序中声明的变量可以作为参数传递给DLL。 四、被调用的DLL必须存在。 这一点很重要,使用静态调用方法时 要求所调用的DLL文件以及要调用的函数或过程等等必须存在。如果不存在或指定的路径和文件名不正确的话,运行主程序时系统会提示“启动程序时出错”或“找不到*.dll文件”等运行错误。 第四章 在Delphi中动态调用DLL top 动态调用DLL相对复杂很多,但非常灵活。为了全面的说明该问题,这次我们举一个调用由C+编写的DLL的例子。首先在C+中编译下面的D

27、LL源程序。 #include extern ”C” _declspec(dllexport int WINAPI TestC(int i return i; 编译后生成一个DLL文件,在这里我们称该文件为Cpp.dll,该DLL中只有一个返回整数类型的函数TestC。为了方便说明,我们仍然引用上面的调用程序,只是将原来的Button1Click过程中的语句用下面的代码替换掉了。 procedure TForm1.Button1Click(Sender: TObject; type TIntFunc=function(i:integer:integer;stdcall; var Th:Than

28、dle; Tf:TIntFunc; Tp:TFarProc; begin Th:=LoadLibrary(Cpp.dll; 装载DLL if Th>0 then try Tp:=GetProcAddress(Th,PChar(TestC; if Tp<>nil then begin Tf:=TIntFunc(Tp; Edit1.Text:=IntToStr(Tf(1; 调用TestC函数 end else ShowMessage(TestC函数没有找到; finally FreeLibrary(Th; 释放DLL end else ShowMessage(Cpp.dll没有找

29、到; end; 大家已经看到了,这种动态调用技术很复杂,但只要修改参数,如修改LoadLibrary(Cpp.dll中的DLL名称为Delphi.dll就可动态更改所调用的DLL。 一、定义所要调用的函数或过程的类型。 在上面的代码中我们定义了一个TIntFunc类型,这是对应我们将要调用的函数TestC的。在其他调用情况下也要做同样的定义工作。并且也要加上stdcall调用参数。 二、释放所调用的DLL。 我们用LoadLibrary动态的调用了一个DLL,但要记住必须在使用完后手动地用FreeLibrary将该DLL释放掉,否则该DLL将一直占用内存直到您退出Windows或关机为止。 现

30、在我们来评价一下两种调用DLL的方法的优缺点。静态方法实现简单,易于掌握并且一般来说稍微快一点,也更加安全可靠一些;但是静态方法不能灵活地在运行时装卸所需的DLL,而是在主程序开始运行时就装载指定的DLL直到程序结束时才释放该DLL,另外只有基于编译器和链接器的系统(如Delphi)才可以使用该方法。动态方法较好地解决了静态方法中存在的不足,可以方便地访问DLL中的函数和过程,甚至一些老版本DLL中新添加的函数或过程;但动态方法难以完全掌握,使用时因为不同的函数或过程要定义很多很复杂的类型和调用方法。对于初学者,笔者建议您使用静态方法,待熟练后再使用动态调用方法。 第五章 使用DLL的实用技巧

31、 top 一、编写技巧。 1 、为了保证DLL的 正确性,可先编写成普通的应用程序的一部分,调试无误后再从主程序中分离出来,编译成DLL。 2、为了保证DLL的通用性,应该在自己编写的DLL中杜绝出现可视化控件的名称,如:Edit1.Text中的Edit1名称;或者自定义非Windows定义的类型,如某种记录。 3 、为便于调试,每个函数和过程应该尽可能短小精悍,并配合具体详细的注释。 4 、应多利用try-finally来处理可能出现的错误和异常,注意这时要引用SysUtils单元。 5 、尽可能少引用单元以减小DLL的大小,特别是不要引用可视化单元,如Dialogs单元。例如一般情况下,我们可以不引用Classes单元,这样可使编译后的DLL减小大约16Kb。 二、调用技巧。 1 、在用静态方法时,可以给被调用的函数或过程更名。在前面提到的C+编写的DLL例子中,如果去掉extern ”C”语句,C+会编译出一些奇怪的函数名,原来的TestC函数会被命名为TestC$s等等可笑的怪名字,这是由于C+采用了C+ name

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论