不是原创,DFW上的:
用Delphi制作DLL
一 Dll的制作一般步骤
二 参数传递
三 DLL的初始化和退出清理[如果需要初始化和退出清理]
四 全局变量的使用
五 调用静态载入
六 调用动态载入
七 在DLL建立一个TForM
八 在DLL中建立一个TMDIChildForM
九 示例:
十 Delphi制作的Dll与其他语言的混合编程中常遇问题:
十一 相关资料一 Dll的制作一般分为以下几步:
1 在一个DLL工程里写一个过程或函数
2 写一个Exports关键字,在其下写过程的名称。不用写参数和调用后缀。
二 参数传递
1 参数类型最好与window C++的参数类型一致。不要用DELPHI的数据类型。
2 最好有返回值[即使是一个过程],来报出调用成功或失败,或状态。成功或失败的返回值最好为1[成功]或0[失败].一句话,与windows c++兼容。
3 用stdcall声明后缀。
4 最好大小写敏感。
5 无须用far调用后缀,那只是为了与windows 16位程序兼容。三 DLL的初始化和退出清理[如果需要初始化和退出清理]
1 DLLProc[SysUtils单元的一个Pointer]是DLL的入口。在此你可用你的函数替换了它的入口。但你的函数必须符合以下要求[其实就是一个回调函数]。如下:
procedure DllEnterPoint(dwReason: DWORD);far;stdcall;
dwReason参数有四种类型:
DLL_PROCESS_ATTACH:进程进入时
DLL_PROCESS_DETACH进程退出时
DLL_THREAD_ATTACH 线程进入时
DLL_THREAD_DETACH 线程退出时
在初始化部分写:
  DLLProc := @DLLEnterPoint;
  DllEnterPoint(DLL_PROCESS_ATTACH);
2 如Form上有TdcomConnection组件,就Uses Activex,在初始化时写一句CoInitialize (nil);
3 在退出时一定保证DcomConnection.Connected := False,并且数据集已关闭。否则报地址错。四 全局变量的使用
在widnows 32位程序中,两个应用程序的地址空间是相互没有联系的。虽然DLL在内存中是一份,但变量是在各进程的地址空间中,因此你不能借助dll的全局变量来达到两个应用程序间的数据传递,除非你用内存映像文件。五 调用静态载入
1 客户端函数声名:
1)大小写敏感。
2)与DLL中的声明一样。
   如: showform(form:Tform);Far;external'yproject_dll.dll';
3)调用时传过去的参数类型最好也与windows c++一样。
4)调用时DLL必须在windows搜索路径中,顺序是:当前目录;Path路径;windows;widows\system;windows\ssystem32;六 调用动态载入
1 建立一种过程类型[如果你对过程类型的变量只是一个指针的本质清楚的话,你就知道是怎么回事了]。如:
type
   mypointer=procedure(form:Tform);Far;external;
var
  Hinst:Thandle;
  showform:mypointer;
begin
   Hinst:=loadlibrary('yproject_dll');//Load一个Dll,按文件名找。
  showform:=getprocaddress(Hinst,'showform');//按函数名找,大小写敏感。如果你知道自动化对象的本质就清楚了。
  showform(application.mainform);//找到函数入口指针就调用。
  Freelibrary(Hinst);
end;七 在DLL建立一个TForM
1 把你的Form Uses到Dll中,你的Form用到的关联的单元也要Uses进来[这是最麻烦的一点,因为你的Form或许Uses了许多特殊的单元或函数]
2 传递一个Application参数,用它建立Form.八 在DLL中建立一个TMDIChildForM
1 Dll中的MDIForm.FormStyle不用为fmMDIChild.
2 在CreateForm后写以下两句:
function ShowForm(mainForm:TForm):integer;stdcall
var
  Form1: TForm1;
  ptr:PLongInt;
begin
  ptr:=@(Application.MainForm);//先把dll的MainForm句柄保存起来,也无须释放,只不过是替换一下
  ptr^:=LongInt(mainForm);//用主调程序的mainForm替换DLL的MainForm。MainForm是特殊的WINDOW,它专门管理Application中的Forms资源.
//为什么不直接Application.MainForm := mainForm,因为Application.MainForm是只读属性
  Form1:=TForm1.Create(mainForm);//用参数建立
end;
备注:参数是主调程序的Application.MainForm九 示例:
DLL源代码:
library Project2;uses
  SysUtils,
  Classes,
  Dialogs,
  Forms,
  Unit2 in 'Unit2.pas' {Form2};{$R *.RES}
var
  ccc: Pchar;procedure OpenForm(mainForm:TForm);stdcall;
var
  Form1: TForm1;
  ptr:PLongInt;
begin
  ptr:=@(Application.MainForm);
  ptr^:=LongInt(mainForm);
  Form1:=TForm1.Create(mainForm);
end;procedure InputCCC(Text: Pchar);stdcall;
begin
  ccc := Text;
end;procedure ShowCCC;stdcall;
begin
  ShowMessage(String(ccc));
end;exports
  OpenForm;
  InputCCC,
  ShowCCC;
begin
end.调用方源代码:
unit Unit1;interfaceuses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Edit1: TEdit;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;var
  Form1: TForm1;implementation{$R *.DFM}
procedure OpenForm(mainForm:TForm);stdcall;External'project2.dll';
procedure ShowCCC;stdcall;External'project2.dll';
procedure InputCCC(Text: Pchar);stdcall;External'project2.dll';procedure TForm1.Button1Click(Sender: TObject);
var
  Text: Pchar;
begin
  Text := Pchar(Edit1.Text);
//  OpenForm(Application.MainForm);//为了调MDICHILD
  InputCCC(Text);//为了实验DLL中的全局变量是否在各个应用程序间共享
end;procedure TForm1.Button2Click(Sender: TObject);
begin
  ShowCCC;//这里表明WINDOWS 32位应用程序DLL中的全局变量也是在应用程序地址空间中,16位应用程序或许不同,没有做实验。
end;十 Delphi制作的Dll与其他语言的混合编程中常遇问题:
1 与PowerBuilder混合编程
  在定义不定长动态数组方面在函数退出清理堆栈时老出现不可重现的地址错,原因未明,大概与PB的编译器原理有关,即使PB编译成二进制代码也如此。

解决方案 »

  1.   

    机械出版的《Delphi5开发人员指南》里面讲的较详细
      

  2.   

    Delphi中动态链接库两种调用方式的比较  摘要:本文阐述了Windows环境下动态链接库的概念和特点,对静态调用和动态调用两种调用方式作出了比较,并给出了Delphi中应用动态链接库的实例。  一、动态链接库的概念  动态链接库(Dynamic Link Library,缩写为DLL)是一个可以被其它应用程序共享的程序模块,其中封装了一些可以被共享的例程和资源。动态链接库文件的扩展名一般是dll,也有可能是drv、sys和fon,它和可执行文件(exe)非常类似,区别在于DLL中虽然包含了可执行代码却不能单独执行,而应由Windows应用程序直接或间接调用。  动态链接是相对于静态链接而言的。所谓静态链接是指把要调用的函数或者过程链接到可执行文件中,成为可执行文件的一部分。换句话说,函数和过程的代码就在程序的exe文件中,该文件包含了运行时所需的全部代码。当多个程序都调用相同函数时,内存中就会存在这个函数的多个拷贝,这样就浪费了宝贵的内存资源。而动态链接所调用的函数代码并没有被拷贝到应用程序的可执行文件中去,而是仅仅在其中加入了所调用函数的描述信息(往往是一些重定位信息)。仅当应用程序被装入内存开始运行时,在Windows的管理下,才在应用程序与相应的DLL之间建立链接关系。当要执行所调用DLL中的函数时,根据链接产生的重定位信息,Windows才转去执行DLL中相应的函数代码。  一般情况下,如果一个应用程序使用了动态链接库,Win32系统保证内存中只有DLL的一份复制品,这是通过内存映射文件实现的。DLL首先被调入Win32系统的全局堆栈,然后映射到调用这个DLL的进程地址空间。在Win32系统中,每个进程拥有自己的32位线性地址空间,如果一个DLL被多个进程调用,每个进程都会收到该DLL的一份映像。与16位Windows不同,在Win32中DLL可以看作是每个进程自己的代码。  二、动态链接库的优点  1. 共享代码、资源和数据   使用DLL的主要目的就是为了共享代码,DLL的代码可以被所有的Windows应用程序共享。  2. 隐藏实现的细节   DLL中的例程可以被应用程序访问,而应用程序并不知道这些例程的细节。  3. 拓展开发工具如Delphi的功能  由于DLL是与语言无关的,因此可以创建一个DLL,被C++、VB或任何支持动态链接库的语言调用。这样如果一种语言存在不足,就可以通过访问另一种语言创建的DLL来弥补。  三、动态链接库的实现方法  1. Load-time Dynamic Linking  这种用法的前提是在编译之前已经明确知道要调用DLL中的哪几个函数,编译时在目标文件中只保留必要的链接信息,而不含DLL函数的代码;当程序执行时,利用链接信息加载DLL函数代码并在内存中将其链接入调用程序的执行空间中,其主要目的是便于代码共享。  2. Run-time Dynamic Linking   这种方式是指在编译之前并不知道将会调用哪些DLL函数,完全是在运行过程中根据需要决定应调用哪个函数,并用LoadLibrary和GetProcAddress动态获得DLL函数的入口地址。  四、DLL的两种调用方式在Delphi中的比较  编写DLL的目的是为了输出例程供其他程序调用,因此在DLL的工程文件中要把输出的例程用Exports关键字引出。在调用DLL的应用程序中,需要声明用到的DLL中的方法,声明格式要和DLL中的声明一样。访问DLL中的例程有静态调用和动态调用两种方式。静态调用方式就是在单元的Interface部分用External指示字列出要从DLL中引入的例程;动态调用方式就是通过调用Windows的API包括LoadLibrary函数、GetProcAddress函数以及FreeLibrary函数动态的引入DLL中的例程。  静态调用方式所需的代码较动态调用方式所需的少,但存在着一些不足,一是如果要加载的DLL不存在或者DLL中没有要引入的例程,这时候程序就自动终止运行;二是DLL一旦加载就一直驻留在应用程序的地址空间,即使DLL已不再需要了。动态调用方式就可解决以上问题,它在需要用到DLL的时候才通过LoadLibrary函数引入,用完后通过FreeLibrary函数从内存中卸载,而且通过调GetProcAddress函数可以指定不同的例程。最重要的是,如果指定的DLL出错,至多是API调用失败,不会导致程序终止。以下将通过具体的实例说明说明这调用方式的使用方法。  1. 静态调用方式  示例程序创建了一个DLL,其中仅包含一个求两个整数的和的函数,在主程序中输入两个整数,通过调用该DLL,即可求出两个整数的和,如图1所示。该DLL的程序代码如下:library AddNum;
    uses
    SysUtils,
    Classes;{$R *.res}function AddNumber(Num1,Num2:integer):integer;stdcall; //定义求和函数
     begin
      result:=Num1+Num2;
     end;
      exports
      AddNumber; //引出求和函数
     begin
    end.   主程序在调用该DLL时,首先在interface部分声明要调用的函数:function AddNum(Num1,Num2:integer):integer;stdcall;external 'AddNum.dll' name 'AddNumber';    然后在按钮控件的事件中写入如下代码:procedure TForm1.Button1Click(Sender: TObject);
    var
     Number1,Number2:integer; 
     Sum:integer;
    begin
     Number1:=strtoint(Edit1.Text);
     Number2:=strtoint(Edit2.Text);
     Sum:=AddNum(Number1,Number2); //调用求和函数计算结果
     Edit3.Text:=inttostr(Sum);
    end; 
    2.动态调用方式  这个示例程序创建了一个显示日期的DLL,其中包含一个窗体,如图2所示。  程序中定义了一个ShowCalendar函数,返回在这个窗体中设定的日期。函数定义如下:function ShowCalendar(AHandle: THandle; ACaption: String): TDateTime;
    var
     DLLForm: TDLLForm;
    begin
     Application.Handle := AHandle;
     DLLForm := TDLLForm.Create(Application); //创建并显示窗体
     try
      DLLForm.Caption := ACaption;
      DLLForm.ShowModal; //显示方式为模式化
      Result := DLLForm.calDLLCalendar.CalendarDate; //返回设定日期
     finally
      DLLForm.Free; //用完后卸载该窗体
     end;
    end;   在DLL的工程文件中用exports ShowCalendar; 语句引出该函数。下面通过一个简单的应用程序测试一下该DLL文件。新建一个工程文件,在窗体中放置一个Label控件和一个按钮控件,在按钮控件的OnClick事件中编写如下代码:procedure TMainForm.Button1Click(Sender: TObject);
    var
     OneHandle : THandle; //定义一个句柄变量
    begin
     OneHandle := LoadLibrary('Clendar.dll'); //动态载入DLL,并返回其句柄
     try
      if OneHandle <> 0 then //如果载入成功则获取ShowCalendar函数的地址
       @ShowCalendar := GetProcAddress(OneHandle, 'ShowCalendar');
       if not (@ShowCalendar = nil) then
        //如果找到该函数则在主窗体的Label1中显示DLL窗体中设定的日期
        Label1.Caption := DateToStr(ShowCalendar(Application.Handle, Caption))
       else
        RaiseLastWin32Error;
     finally
      FreeLibrary(OneHandle); //调用完毕收回DLL占用的资源
     end;
    end;   从以上程序中可以看到DLL的动态调用方式比静态调用方式的优越之处。DLL例程在用到时才被调入,用完后就被卸载,大大减少了系统资源的占用。在调用LoadLibrary函数时可以明确指定DLL的完整路径,如果没有指定路径,运行时首先查找应用程序载入的目录,然后是Windows系统的System目录和环境变量Path设定的路径。  五、结束语  由于动态链接库可以实现代码和资源的共享,大大减少系统资源的占用,因此在当今的应用程序开发中起着非常重要的作用。Delphi是现今流行的应用软件开发工具,本文就如何在Delphi中使用动态链接库给出了一定程度上的阐述。
      

  3.   

    Delphi创建及调用DLL文件    现时系统的开发,多数都在几人以上的组合,合作方式开发,这样也方便系统的快速开发目的。而 DLL 的方法最为方便。我现整理了一些这方面资料,希望能帮助一些有需要的同学。一.函数过程的写法:library FIRSTDLL;
    uses
      SysUtils, Classes;
    {$R *.RES}
    // 1.定义函数具体过程和输出接口方式
    // --------------------------------
    // 函数 1
    // 功能:事数据3倍放大函数
    // --------------------------------
    function BBnToSSnn(SourceResult:Integer):Integer;stdCall;
    begin
      if SourceResult>0 then
        Result:=SourceResult+3 //结果存放于Result
      else
        Result:=SourceResult;
    end;exports 
      BBnToSSnn; //2.函数输出口定义end.二.在DLL中创建Form=======================
    1.一步,创建DLL工程,及加入设置好的Formlibrary MGRPERSN;
    uses
      SysUtils,  Classes,
      MGRPERFM in 'MGRPERFM.pas' {FormPERSON};//1.Form的代码(与一般的Form一样){$R *.RES}
    exports
       ShowPerSN;//2.函数输出口定义
    begin
    end.2. 在DLL设定的Form的设置 ===========================================
    unit MGRPERFM;
    interface
    uses
      Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
      ComCtrls, ToolWin, ImgList;
    type
      TFormPERSON = class(TForm)
      private
        { Private declarations }
      public
        { Public declarations }
      end;
    //此处的变量不再用,给其改个地方,如下(改变之一)
    //var 
    //  FormPERSON: TFormPERSON;
    {宣布Form函数出口}//改变之二
    function ShowPerSN(AHandle: THandle; ACaption: String):BOOL; StdCall;implementation
    {$R *.DFM}//函数据过程定义
    function ShowPerSN(AHandle: THandle; ACaption: String):BOOL;
    var
      FormPERSON: TFormPERSON; //定义窗体类(上面的放到了此处)
    begin
      //拷贝应用程式句柄给DLL的应有程式对象
      Application.Handle := AHandle;
      FormPERSON := TFormPERSON.Create(Application);//创建控件TForm
      try
        FormPERSON.Caption := ACaption;
        FormPERSON.ShowModal;//显示此Form
        Result := False; //反回成功值
      finally
        FormPERSON.Free;
      end;
    end;三.DLL中函数及窗体的调用==========================
    1.调用方法一
    --------------
    implementation //在此的下方写明调用函数的DLL
    {$R *.DFM}
    //DLL内函数调用
    function BBnToSSnn(SourceResult:Integer):Integer; StdCall external 'FIRSTDLL.DLL';2.调用方法二
    ---------------
    type  //在此创建一个函数类
      // 1 -------------------------------
      TShowPerSN = function (AHandle: THandle; ACaption: String): BOOL; StdCall;
      EDLLLoadError = class(Exception);//同时分创建一个出错记录类
      // 1 -------------------------------
      TMAINCLTR = class(TForm) //这里不变,系统自动生成
    ......procedure TMAINCLTR.ToolButton1Click(Sender: TObject);
    var  //按钮的调用事件:调用过程
      LibHandle: THandle;
      ShowPerSN: TShowPerSN;
    begin
      Application.Title:='人力资源管理系统DLL文件测试程式';
      { Attempt to load the DLL 尝试装入DLL文件}
      LibHandle := LoadLibrary('MGRPERSN.DLL');
      try
        if LibHandle = 0 then
          raise EDLLLoadError.Create('无法成功装入MGRPERSN.DLL');
        @ShowPerSN := GetProcAddress(LibHandle, 'ShowPerSN');
        if not (@ShowPerSN = nil) then
          ShowPerSN(Application.Handle, '人事资料管理')//呼叫出窗体
        else
          RaiseLastWin32Error;
      finally
        FreeLibrary(LibHandle); // Unload the DLL.
      end;
    end;
    ============== END ==================