射手的博客
学习Delphi,需要一个记录学习的博客,

 

                   Delphi Libraries(Delphi )

简介:

Object Pascal 中“动态掉入库(dynamically loadable library)”

    Windows下 以名称为“动态链接库” 后缀名为“.DLL” 形式的文件出现在系统当中.

    Linux   下以名称为“共享目标库”后缀名以“.so” 形式的文件出现在系统当中.

本文主要讲解的是在Windows 中使用 Borland Delphi 开发软件 进行对 动态链接库-DLL 规则讲解.

Libraries (库)的定义它是一个例程的集合.

Libraries (库)的形式像单元文件一样“动态链接库包含共享的代码和资源,但这个库是一个可以单独编译的可执行文件,它在运行时被连接到使用它的程序当中.

 

调用动态链接库:

   Delphi 当中 Windows 系统例程(Windows API) 存放在Uses 的子单元当中.事先就给开发者们把API函数连接到了Delphi的系统单元文件当中(系统单元是不可见的).方便调用.

你可以直接调用操作系统的例程,但他们直到运行时才被连接到你的程序当中.这说明在编译程序时它们不会被编译验证.

这也说明当你调入一个“动态链接库中的例程是不会被编译验证的.当你调用外部“动态链接库”,如果“动态链接库没有相应的例程则会出现无法定位而报错.

 调用动态链接库有两种调用方法: 1.静态调入 2.动态调入

 1.静态调入

 使用 external 指示字声明动态链接库名称.

 Windows 下: procedure DoSomething; external 'MYLIB.DLL';

 Linux 下:    procedure DoSomething; external 'mylib.so';  

需要注意的有:

1.MyLIB.DLL 或者 mylib.so 2个文件必须在程序的根目录下才能顺利调入.

2.例程名也必须与“动态链接库当中的例程名相同才能顺利的调入.

若你在程序中包含这个声明,“动态掉入库在程序启动时被调入一次,在程序的整个运行期间,标志符 DoSomething 总是指同一个共享库中的同一个入口点。

静态调入的缺点:例程占用内存直到程序结束.

2.动态调入

 动态调入就是利用操作系统中的库函数(API函数)来调入一个外部动态链接库中的例程.

通常我们使用 LoadLibrary,FreeLibrary,GetProcAddress. 3个函数来动态调用一个外部的“动态链接库

{3个函数的使用方法我已经详细的写在了博客当中.连接地址为:

http://www.cnitblog.com/Archer/archive/2009/03/28/55831.html}

动态调入的优点:解决了静态调用时,例程占用内存的问题.

 

 

Writing dynamically loadable libraries(编写动态调入库)

 

Writing dynamically loadable libraries(编写动态调入库)

   动态链接库与程序文件唯一不同的就是以关键字Library 开始.取代了(program).

 编写动态链接库需要注意以下几点.

1.只有被库明确输出(exports)的例程才能被其他库或程序导入.

2. 若要你的库对其它语言编写的程序是可见的,最安全的办法是在声明输出函数时指定 stdcal 调用约定,其它语言或许不支持 Object Pascal 默认的 register调用约定

 

库的源文件通常简化为包含一个 uses 子句、一个 exports 子句和初始化代码(指的是过程和函数的block代码)

 

The exports clauseexports 子句)

当一个例程在 exports子句中列出时,它将被输出,才能被其他库或程序 导入,调用.

exports entry1, ..., entryn;

每个 entry可以是一个过程、函数或变量(它必须在 exports 子句之前声明)的名称,后面跟参数列表(只有当输出重载的例程时)和一个可选的 name 说明符,你可以使用单元名限定过程或函数的名称。

需要注意的有:

1. “动态链接库输出例程建立不要使用索引说明符.

2. 名称说明符包括指示字name 后面跟一个字符串常量. entry没有名称说明符,例程被输出时使用声明的原始名称,包括拼写和大小写。

 exports

 DoSomethingABC name 'DoSomething';

当在动态调入库中输出重载的函数或过程时,你必须在 exports 子句中指定它的参数列表,

exports

 Divide(X, Y: Integer) name 'Divide_Ints',

 Divide(X, Y: Real) name 'Divide_Reals';

 

Library initialization code(库初始化代码)

一个库的块(block)所包含的语句构成了库的初始化代码,每当库被调入时,这些代码执行一次。

Global variables in a library(库中的全局变量)

在库中声明的全局变量不能被object Pascal 程序导入.

一个库一次能被多个程序使用(调用),每一个程序都把这个库加载到自己进程空间(内存空间)(这是一个库的拷贝),且每个拷贝有自己的全局变量集合。对于在多个库间(或一个库的多个实例间)共享内存,它们必须使用内存映射文件。

这里说明库是被加载到内存当中的.在内存中文件是以16进制定位内存地址的.

 

 

Libraries and system variables(库和系统变量)

 System单元声明的几个变量对自己引入的程序库有特殊影响.

1.IsLibrary 变量

IsLibrary变量是在System.pas单元中定义的全局标志变量之一。

使用 IsLibrary 变量来确定代码是作为<程序>还是<>执行,IsLibrary 在程序中总是 False,在库中总是 True;

如果IsLibrary的值为True则表明程序模块(自身)是一个动态链接库,反之就是一个可执行程序。

  此函数常用来判断调用的程序模块(自身)是“动态链接库”或者是“可执行程序”.

 2. Hinstance (Handle instance 句柄实例)

hinstance是这个<应用程序><实例句柄>,也就是系统将可执行文件的映象加载到进程空间的基本地址,如果需要程序内包含的<资源(如图片资源)>的时候就需要这个实例句柄了。

当你启动一个程序时,操作系统会给这个程序起一个名字,这个名字就叫 Handle
当你启动一个程序时,操作系统会将这个程序装载到某个内存空间,这个空间的起始地址就是HInstance。在 NT 系统这个 HInstance 一般都是 400000h

Handlewindows系统里的句柄,是系统识别目标对象的一个标识,对于整个操作系统来说各句柄应该是唯一的,不会重复。而且Handle一般是由Windows系统分配的,程序无法控制。
HInstance 是程序内部的句柄,实际值即Windows系统给应用程序分配的内存基址,也是该程序体内(包括主体EXE和调用的DLL)相互识别调用的一个标识,对于程序内部来书说,HInstance是唯一的,不会重复。但对于Windows系统来说,各程序EXEHInstance大部分可以说是一样,一般都是默认的$00400000。这个值在大多数编译器中都可以修改。如DELPHI里可以在工程Options->Link->ImageBase里修改。
简单来说,大概应该可以这样来理解,Handle可以说是Windows对象句柄,HInstance可以说是程序模块句柄。 

 

参考: {http://www.delphibbs.com/delphibbs/dispq.asp?lid=3136263}

 

3. DLLProc

 DLLProc 变量允许一个库监测(监视)操作系统对它的入口点(entry point)的调用,这个特征通常只是由支持<多线程>的库使用。

在过程体中,你能依据哪个参数被传递给过程来指定要采取地行动。

要监测操作系统调用,创建一个回调过程,它接收一个整数参数,

此函数有以下几个参数

  DLL_Process_Attach:  //整个DLL的初始化代码 
  DLL_Process_Detach:  //该值表示库作为完全退出的结果或调用FreeLibrary的结果,从调用库的进程中被分离。 {表示对DLL的善后代码} 
  DLL_Thread_Attach:  //该值表示当前进程创建了一个新的线程(仅适用于Windows)。

  DLL_Thread_Detach:  //该值表示线程完全退出(仅适用于Windows

 

Exceptions and runtime errors in libraries(库的异常和运行时错误) 

 当在动态链接库中有发生异常,<但没有处理时>,它把异常传播到库的外面到达调用者.则出现错误提示,导致程序结束.

 如果调用程序或库是用 object Pascal 编写的可通过用tey...except 语句处理这些异常.

 

如果调用程序或库是用其它语言编写的,异常被当作操作系统的异常(异常代码:$0EEDFACE)进行处理。在操作系统异常记录的 ExceptionInformation 数组的第一个入口中,包含了异常地址,第二个入口包含一个指向 Object Pascal 异常对象的引用。

通常,你不应该使异常扩散到库的外面。在 Windows 下,Delphi 异常映射到操作系统的异常模型

 

若一个库没有使用 SysUtils 单元,它不支持异常处理。这种情况下,若库发生运行时错误,调用程序将终止。因为库没有办法知道它是否从一个 Object Pascal 程序进行调用,它不能调用程序的退出过程,程序只是简单地被终止,并从内存中清除。

 

Shared-memory manager (共享内存管理器)

当使用Delphi 自带的内存管理器时:

Windows 如果DLL 输出的例程是长字符串或者动态数组作为参数或者作为函数返回值(不管是直接的,还是通过记录或对象封装的),那么DLL 和它的客户端程序(DLL)必须使用ShareMem 单元.

(由于库和程序之间内存管理的管理域限制,所以需要申请ShareMem单元,使他们能够共用一个内存管理器)

 

当一个程序或 DLL 调用 New GetMem分配内存,而在另一个模块中调用 Dispose FreeMem来释放内存时。需要用到ShareMem单元,.

ShareMem单元应当在程序或库的 uses子句中第一个列出。

 

ShareMem  BORLANDMM.DLL 内存管理器的接口单元,它允许在模块间共享动态分配的内存。

BORLANDMM.DLL必须连同使用ShareMem单元的程序和DLL一同发布。 当程序或DLL使用ShareMem

时,它的内存管理器被 BORLANDMM.DLL 中的取代。

 

现在常用的内存管理器有FastShareMem



为了方便阅读提供PFD版本: http://www.cnitblog.com/Files/Archer/Delphi%20library.zip
posted on 2009-04-13 11:20 Archer 阅读(255) 评论(0)  编辑 收藏 引用 所属分类: Libraries and packages(库和包)
只有注册用户登录后才能发表评论。