在 21天教会你用Delphi 书里 有详细的介绍!
 很详细的 ! 但是英文的! 不过很还看的懂!

解决方案 »

  1.   

    DELPHI中调用DLL的方法和一些注意事项和技巧
    第一章 为什么要使用动态链接库(DLL)提起DLL您一定不会陌生,在Windows中有着大量的以DLL为后缀的文件,它们是保证Windows正常运行和维护升级的重要保证。(举个例子,笔者的Win95 System目录下尽有500多个DLL文件。)其实,DLL是一种特殊的可执行文件。说它特殊主要是因为一般它都不能直接运行,需要宿主程序比如*.EXE程序或其他DLL的动态调用才能够使用。简单的说,在通常情况下DLL是经过编译的函数和过程的集合。使用DLL技术主要有以下几个原因:一、 减小可执行文件大小。   DLL技术的产生有很大一部分原因是为了减小可执行文件的大小。当操作系统进入Windows时代后,其大小已经达到几十兆乃至几百兆。试想如果还是使用DOS时代的单执行文件体系的话一个可执行文件的大小可能将达到数十兆,这是大家都不能接受的。解决的方法就是采用动态链接技术将一个大的可执行文件分割成许多小的可执行程序。二、 实现资源共享。这里指的资源共享包括很多方面,最多的是内存共享、代码共享等等。早期的程序员经常碰到这样的事情,在不同的编程任务中编写同样的代码。这种方法显然浪费了很多时间,为了解决这个问题人们编写了各种各样的库。但由于编程语言和环境的不同这些库一般都不能通用,而且用户在运行程序时还需要这些库才行,极不方便。DLL的出现就像制定了一个标准一样,使这些库有了统一的规范。这样一来,用不同编程语言的程序员可以方便的使用用别的编程语言编写的DLL。另外,DLL还有一个突出的特点就是在内存中只装载一次,这一点可以节省有限的内存,而且可以同时为多个进程服务。三、 便于维护和升级。细心的朋友可能发现有一些DLL文件是有版本说明的。(查看DLL文件的属性可以看到,但不是每一个DLL文件都有)这是为了便于维护和升级。举个例子吧,早期的Win95中有一个BUG那就是在闰年不能正确显示2月29日这一天。后来,Microsoft发布了一个补丁程序纠正了这个BUG。值得一提的是,我们并没有重装Win95,而是用新版本的DLL代替了旧版本的DLL。(具体是哪一个DLL文件笔者一时想不起来了。)另一个常见的例子是驱动程序的升级。例如,著名的DirectX就多次升级,现在已经发展到了6.0版了。更妙的是,当我们试图安装较低版本的DLL时,系统会给我们提示,避免人为的操作错误。例如我们升级某硬件的驱动程序时,经常碰到Windows提示我们当前安装的驱动程序比原来的驱动程序旧。四、 比较安全。这里说的安全也包括很多方面。比如,DLL文件遭受病毒的侵害机率要比普通的EXE文件低很多。另外,由于是动态链接的,这给一些从事破坏工作的“高手”们多少带来了一些反汇编的困难。第二章 在Delphi中编写DLL top注意:在这里笔者假定读者使用的是Delphi 3或Delphi 4开场白说了那么多,总该言归正传了。编写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函数。只要编译上面的代码,就可以得到一个名为Delphi.dll的动态链接库。现在,让我们来看看有哪些需要注意的地方。一、 在DLL中编写的函数或过程都必须加上stdcall调用参数。在Delphi 1或Delphi 2环境下该调用参数是far。从Delphi3以后将这个参数变为了stdcall,目的是为了使用标准的Win32参数传递技术来代替优化的register参数。忘记使用stdcall参数是常见的错误,这个错误不会影响DLL的编译和生成,但当调用这个DLL时会发生很严重的错误,导致操作系统的死锁。原因是register参数是Delphi的默认参数。二、 所写的函数和过程应该用exports语句声明为外部函数。正如大家看到的,TestDll函数被声明为一个外部函数。这样做可以使该函数在外部就能看到,具体方法是单激鼠标右键用“快速查看(Quick View)”功能查看该DLL文件。(如果没有“快速查看”选项可以从Windows CD上安装。)TestDll函数会出现在Export Table栏中。另一个很充分的理由是,如果不这样声明,我们编写的函数将不能被调用,这是大家都不愿看到的。三、 当使用了长字符串类型的参数、变量时要引用ShareMem。Delphi中的string类型很强大,我们知道普通的字符串长度最大为256个字符,但Delphi中string类型在默认情况下长度可以达到2M。(对,您没有看错,确实是两兆。)这时,如果您坚持要使用string类型的参数、变量甚至是记录信息时,就要引用ShareMem单元,而且必须是第一个引用的。既在uses语句后是第一个引用的单元。如下例:
    uses
    ShareMem,
    SysUtils,
    Classes;
    还有一点,在您的工程文件(*.dpr)中而不是单元文件(*.pas)中也要做同样的工作,这一点Delphi自带的帮助文件没有说清楚,造成了很多误会。不这样做的话,您很有可能付出死机的代价。避免使用string类型的方法是将string类型的参数、变量等声明为Pchar或ShortString(如:s:string[10])类型。同样的问题会出现在当您使用了动态数组时,解决的方法同上所述。第三章 在Delphi中静态调用DLL top调用一个DLL比写一个DLL要容易一些。首先给大家介绍的是静态调用方法,稍后将介绍动态调用方法,并就两种方法做一个比较。同样的,我们先举一个静态调用的例子。unit Unit1;
    interface
    uses
    Windows, Messages, SysUtils, Classes, Graphics,
    Controls, Forms, Dialogs, StdCtrls;
    type
    TForm1 = class(TForm)
    Edit1: TEdit;
    Button1: TButton;
    procedure 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(TestDll(1));
    end;
    end.上面的例子中我们在窗体上放置了一个编辑框(Edit)和一个按钮(Button),并且书写了很少的代码来测试我们刚刚编写的Delphi.dll。大家可以看到我们唯一做的工作是将TestDll函数的说明部分放在了implementation中,并且用external语句指定了Delphi.dll的位置。(本例中调用程序和Delphi.dll在同一个目录中。)让人兴奋的是,我们自己编写的TestDll函数很快被Delphi认出来了。您可做这样一个实验:输入“TestDll(”,很快Delphi就会用fly-by提示条提示您应该输入的参数是什么,
    就像我们使用Delphi中定义的其他函数一样简单。注意事项有以下一些:一、 调用参数用stdcall。和前面提到的一样,当引用DLL中的函数和过程时也要使用stdcall参数,原因和前面提到的一样。二、 用external语句指定被调用的DLL文件的路径和名称。正如大家看到的,我们在external语句中指定了所要调用的DLL文件的名称。没有写路径是因为该DLL文件和调用它的主程序在同一目录下。如果该DLL文件在C:\u65292,则我们可将上面的引用语句写为external 'C:.dll'。注意文件的后缀.dll必须写上。三、 不能从DLL中调用全局变量。如果我们在DLL中声明了某种全局变量,如:var s:byte 。这样在DLL中s这个全局变量是可以正常使用的,但s不能被调用程序使用,既s不能作为全局变量传递给调用程序。不过在调用程序中声明的变量可以作为参数传递给DLL。四、 被调用的DLL必须存在。这一点很重要,使用静态调用方法时要求所调用的DLL文件以及要调用的函数或过程等等必须存在。如果不存在或指定的路径和文件名不正确的话,运行主程序时系统会提示“启动程序时出错”或“找不到*.dll文件”等运行错误。第四章 在Delphi中动态调用DLL top动态调用DLL相对复杂很多,但非常灵活。为了全面的说明该问题,这次我们举一个调用由C++编写的DLL的例子。首先在C++中编译下面的DLL源程序。
    #include<windows.h>
    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:Thandle;
    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没有找到');
    end;大家已经看到了,这种动态调用技术很复杂,但只要修改参数,如修改LoadLibrary('Cpp.dll')中的DLL名称为'Delphi.dll'就可动态更改所调用的DLL。一、 定义所要调用的函数或过程的类型。在上面的代码中我们定义了一个TIntFunc类型,这是对应我们将要调用的函数TestC的。在其他调用情况下也要做同样的定义工作。并且也要加上stdcall调用参数。二、 释放所调用的DLL。我们用LoadLibrary动态的调用了一个DLL,但要记住必须在使用完后手动地用FreeLibrary将该DLL释放掉,否则该DLL将一直占用内存直到您退出Windows或关机为止。
    现在我们来评价一下两种调用DLL的方法的优缺点。静态方法实现简单,易于掌握并且一般来说稍微快一点