精华内容
下载资源
问答
  • 制作DLL

    千次阅读 2011-07-28 14:42:26
    一、开使你的第一个DLL专案 1.File->Close all->File->New﹝DLL﹞代码: //自动产生Code如下 library Project2; //这有段废话 uses SysUtils, Classes; {$R *.RES}
     一、开使你的第一个DLL专案 
    
      1.File->Close all->File->New﹝DLL﹞
    代码:
      //自动产生Code如下
      library Project2;
      //这有段废话
      uses
      SysUtils,
      Classes;

      {$R *.RES}

      begin
      end.
      2.加个Func进来:
      代码:
      library Project2;
      uses
      SysUtils,
      Classes;

    Function MyMax ( X , Y : integer ) : integer ; stdcall ;
    begin
       if X > Y then
       Result := X
       else
       Result := Y ;
    end ;
    //切记:Library 的名字大小写没关系,可是DLL-Func的大小写就有关系了。
    // 在 DLL-Func-Name写成MyMax与myMAX是不同的。如果写错了,立即
    // 的结果是你叫用到此DLL的AP根本开不起来。
    //参数的大小写就没关系了。甚至不必同名。如原型中是 (X,Y:integer)但引
    // 用时写成(A,B:integer),那是没关系的。
    //切记:要再加个stdcall。书上讲,如果你是用Delphi写DLL,且希望不仅给
    // Delphi-AP也希望BCB/VC-AP等使用的话,那你最好加个Stdcall ; 的指示
    //参数型态:Delphi有很多种它自己的变量型态,这些当然不是DLL所喜欢的
    // ,Windows/DLL的母语应该是C。所以如果要传进传出DLL的参数,我们
    // 尽可能照规矩来用。这两者写起来,后者会麻烦不少。如果你对C不熟
    // 的话,那也没关系。我们以后再讲。

      {$R *.RES}

      begin
      end.
      3.将这些可共享的Func送出DLL,让外界﹝就是你的Delphi-AP啦﹞使用:光如此,你的AP还不能用到这些,你还要加个Exports才行。
      代码:
      {$R *.RES}
      exports
      MyMax ;
      begin
      end.
      4.好了,可以按 Ctrl-F9编译了。此时可不要按F9。DLL不是EXE┌不可单独执行的,如果你按F9,会有ErrorMsg的。这时如果DLL有Error,请修正之。再按Ctrl-F9。此时可能有Warning,不要紧,研究一下,看看就好。再按Ctrl-F9,此时就『Done , Compiled 』。同目录就会有个 *.dll 。恭喜,大功告成了。
    二、进行测试:开个新application:
      1.加个TButton
      代码:
      ShowMessage ( IntToStr(MyMax(30,50)) ) ;
      2.告知Exe到那里抓个Func
      代码:
      //在Form,interface,var后加
      Function MyMax ( X , Y : integer ) : integer ; stdcall ; external 'MyTestDLL.dll' ;
      // MyTestDLL.dll为你前时写的DLL项目名字
      // DLL名字大小写没关系。不过记得要加 extension的 .DLL。在Win95或NT,
      // 是不必加 extension,但这两种OS,可能越来越少了吧。要加extension
      可以了,简单吧。
      上面的例子是不是很简单?熟悉Delphi的朋友可以看出以上代码和一般的Delphi程序的编写基本是相同的,只是在TestDll函数后多了一个stdcall参数并且用exports语句声明了TestDll函数。只要编译上面的代码,就可以玫揭桓雒狣elphi.dll的动态链接库。现在,让我们来看看有哪些需要注意的地方:
      1.在DLL中编写的函数或过程都必须加上stdcall调用参数。在Delphi 1或Delphi 2环境下该调用参数是far。从Delphi 3以后将这个参数变为了stdcall,目的是为了使用标准的Win32参数传递技术来代替优化的register参数。忘记使用stdcall参数是常见的错误,这个错误不会影响DLL的编译和生成,但当调用这个DLL时会发生很严重的错误,导致操作系统的死锁。原因是register参数是Delphi的默认参数。
      2.所写的函数和过程应该用exports语句声明为外部函数。
      正如大家看到的,TestDll函数被声明为一个外部函数。这样做可以使该函数在外部就能看到,具体方法是单激鼠标右键用“快速查看(Quick View)”功能查看该DLL文件。(如果没有“快速查看”选项可以从Windows CD上安装。)TestDll函数会出现在Export Table栏中。另一个很充分的理由是,如果不这样声明,我们编写的函数将不能被调用,这是大家都不愿看到的。
      3.当使用了长字符串类型的参数、变量时要引用ShareMem。
      Delphi中的string类型很强大,我们知道普通的字符串长度最大为256个字符,但Delphi中string类型在默认情况下长度可以达到2G。(对,您没有看错,确实是两兆。)这时,如果您坚持要使用string类型的参数、变量甚至是记录信息时,就要引用ShareMem单元,而且必须是第一个引用的。既在uses语句后是第一个引用的单元。如下例:
      uses
      ShareMem,  SysUtils,  Classes;
    还有一点,在您的工程文件(*.dpr)中而不是单元文件(*.pas)中也要做同样的工作,这一点Delphi自带的帮助文件没有说清楚,造成了很多误会。不这样做的话,您很有可能付出死机的代价。避免使用string类型的方法是将string类型的参数、变量等声明为Pchar或ShortString(如:s:string[10])类型。同样的问题会出现在当您使用了动态数组时,解决的方法同上所述。
    用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;
      interface
      uses
        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编译成二进制代码也如此。
    在Delphi中静态调用DLL
      调用一个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:/,则我们可将上面的引用语句写为external ’C:/Delphi.dll’。注意文件的后缀.dll必须写上。
    三、不能从DLL中调用全局变量
      如果我们在DLL中声明了某种全局变量,如:var s:byte 。这样在DLL中s这个全局变量是可以正常使用的,但s不能被调用程序使用,既s不能作为全局变量传递给调用程序。不过在调用程序中声明的变量可以作为参数传递给DLL。
    四、被调用的DLL必须存在
      这一点很重要,使用静态调用方法时要求所调用的DLL文件以及要调用的函数或过程等等必须存在。如果不存在或指定的路径和文件名不正确的话,运行主程序时系统会提示“启动程序时出错”或“找不到*.dll文件”等运行错误。
    在Delphi中动态调用DLL
      动态调用DLL相对复杂很多,但非常灵活。为了全面的说明该问题,这次我们举一个调用由C++编写的DLL的例子。首先在C++中编译下面的DLL源程序。
    #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: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的方法的优缺点。静态方法实现简单,易于掌握并且一般来说稍微快一点,也更加安全可靠一些;但是静态方法不能灵活地在运行时装卸所需的DLL,而是在主程序开始运行时就装载指定的DLL直到程序结束时才释放该DLL,另外只有基于编译器和链接器的系统(如Delphi)才可以使用该方法。动态方法较好地解决了静态方法中存在的不足,可以方便地访问DLL中的函数和过程,甚至一些老版本DLL中新添加的函数或过程;但动态方法难以完全掌握,使用时因为不同的函数或过程要定义很多很复杂的类型和调用方法。对于初学者,笔者建议您使用静态方法,待熟练后再使用动态调用方法。
    使用DLL的实用技巧
    一、编写技巧
      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 mangling技术。这个函数名在Delphi中是非法的,我们可以这样解决这个问题:
    改写引用函数为
    function TestC(i:integer):integer;stdcall;
    external ’Cpp.dll’;name ’@TestC$s’;
    其中name的作用就是重命名。
      2 、可把我们编写的DLL放到Windows目录下或者Windows/system目录下。这样做可以在external语句中或LoadLibrary语句中不写路径而只写DLL的名称。但这样做有些不妥,这两个目录下有大量重要的系统DLL,如果您编的DLL与它们重名的话其后果简直不堪设想,况且您的编程技术还不至于达到将自己编写的DLL放到系统目录中的地步吧!
    三、调试技巧
      1 、我们知道DLL在编写时是不能运行和单步调试的。有一个办法可以,那就是在Run|parameters菜单中设置一个宿主程序。在Local页的Host Application栏中添上宿主程序的名字就可进行单步调试、断点观察和运行了。
      2 、添加DLL的版本信息。开场白中提到了版本信息对于DLL是很重要的,如果包含了版本信息,DLL的大小会增加2Kb。增加这么一点空间是值得的。很不幸我们如果直接使用Project|options菜单中Version选项是不行的,这一点Delphi的帮助文件中没有提到,经笔者研究发现,只要加一行代码就可以了。如下例:
    library Delphi;

    uses
      SysUtils,  Classes;

    {$R *.RES}
    //注意,上面这行代码必须加在这个位置

    function TestDll(i:integer):integer;stdcall;
    begin
      Result:=i;
    end;

    exports
      TestDll;

    begin
    end.
    3 、为了避免与别的DLL重名,在给自己编写的DLL起名字的时候最好采用字符数字和下划线混合的方式。如:jl_try16.dll。
      4 、如果您原来在Delphi 1或Delphi 2中已经编译了某些DLL的话,您原来编译的DLL是16位的。只要将源代码在新的Delphi 3或Delphi 4环境下重新编译,就可以得到32位的DLL了。
    [后记]:除了上面介绍的DLL最常用的使用方法外,DLL还可以用于做资源的载体。例如,在Windows中更改图标就是使用的DLL中的资源。另外,熟练掌握了DLL的设计技术,对使用更为高级的OLE、COM以及ActiveX编程都有很多益处。
     对使用Delphi制作DLL复用文件的建议
     
    在公司里有一些需要制作DLL的场合,因为熟悉、方便和简易,大多数使用Delphi来制作。现在就这个主题提出一些个人建议。
    尽量使用标准DLL接口。指的是传递的参数类型及函数返回类型不能是Delphi特有的,比如string(AnsiString),以及动态数组和含有这些类型成员的复合类型(如记录),也不能是包含有这些类型成员数据成员的对象类型,以避免可能的错误。如果使用了string类型或动态数组类型,且调用方不是Delphi程序,则基本上会报错。如果调用方是Delphi但调用方或被调用方没有在工程文件的第一包含单元不是ShareMem,也可能会出错。
      如果调用方是Delphi应用程序,则可能可以使用不包含禁止类型(string, 动态数组)数据成员的对象作为参数或返回值,但也应尽量避免。
      如果调用方与被调用方都是Delphi程序,而且要使用string或动态数组作参数,则双方工程文件的第一包含单元必须是ShareMem。(C++Builder程序的情况可能与此相同,不过没有测试过。)
      如果调用方不是Delphi程序,则string、动态数组、包含string或动态数组的复合数据类型及类实例,都不能作为参数及返回值。
      因此,为了提高DLL的复用范围,避免可能存在的错误,应当使用标准WIN32 API标准参数类型,以前使用string的变量,可以使用PChar(s)转换。动态数组则转换为指针类型(@array[0]),并加上数组的长度。
      如果因为调用方与被调用方都是Delphi程序,为了编写方便,不想进行上述转换,则推荐使用运行时包的形式。运行时包可以保证动态分配数据的正确释放。这样因为其扩展名(.bpl),显出该文件仅限于Delphi/C++Builder使用(不象DLL)。
      其次,尽量避免使用overload的函数/过程作输出,如果同一操作有多个方式,则可以让函数/过程名有少许差别,类似于Delphi中的FormatXXXX、CreateXXXX等函数及方法,如CreateByDefaultFile, CreateDefault。
      最后,作为DLL的提供者,应当提供直接编程的接口文件,如Delphi中的.pas或.dcu(最好是.pas,因为可以有注释)、C及C++中的.h和.lib。而不是让使用者们自己创建。如果非要有overload的函数/过程,这一点显得特别重要。另外,作为Delphi应用,提供的.pas文件可以是提前连接的(使用external指定DLL中的输出函数),也可以是后期连接的(使用LoadLibrary、GetProcAddress),DLL提供者提供编程接口文件,既显得正式(或HiQoS),又有保障

    展开全文
  • vb制作DLL并引用DLL

    2019-03-04 10:36:29
    详细介绍vb制作DLL以及引用DLL的步骤流程,使用者可以清楚的了解动态链接库的使用方法
  • 制作DLL图标制作DLL图标制作DLL图标制作DLL图标制作DLL图标制作DLL图标制作DLL图标制作DLL图标制作DLL图标
  • 制作DLL文件

    2013-11-21 17:42:19
    制作DLL文件以及使用方法,并在DOS下进行文件的编译。
  • 用VC++制作DLL教程

    2011-04-28 09:19:53
    用VC制作DLL教程用VC制作DLL教程用VC制作DLL教程用VC制作DLL教程用VC制作DLL教程用VC制作DLL教程用VC制作DLL教程用VC制作DLL教程用VC制作DLL教程用VC制作DLL教程用VC制作DLL教程用VC制作DLL教程用VC制作DLL教程用VC...
  • 教案C#制作dll文件.pdf

    2021-10-06 00:13:27
    教案C#制作dll文件.pdf
  • 共有PB 两部分代码,Dll制作的demo ,与调用C#dll,自身的dll的demo; C# 代码两部分,生产dll ,调用dll .
  • monodevelop引用dll及制作dll

    千次阅读 2016-04-13 16:31:12
    使用monodevelop 制作dll时需要引用别的dll 时需要将此添加到引用库(references)中: 新建一个Library(类库) 这几步的目的是把所引用的别的dll 导入进来; 完成之后即可使用dll里的类; ...

    使用monodevelop 制作dll时需要引用别的dll 时需要将此添加到引用库(references)中:


    新建一个Library(类库)




    这几步的目的是把所引用的别的dll 导入进来;

    完成之后即可使用dll里的类;

    把需要制作dll的代码完成之后,build 一下工程,在bin/Debug即可找到制作的dll。

    展开全文
  • Delphi制作DLL

    2013-10-15 09:51:25
    Delphi制作DLL 一 Dll的制作一般步骤 二 参数传递 三 DLL的初始化和退出清理[如果需要初始化和退出清理] 四 全局变量的使用 五 调用静态载入 六 调用动态载入 七 在DLL建立一个TForM 八 在DLL中建立...

    Delphi制作DLL

    一 Dll的制作一般步骤

    二 参数传递

    三 DLL的初始化和退出清理[如果需要初始化和退出清理]

    四 全局变量的使用

    五 调用静态载入

    六 调用动态载入

    七 在DLL建立一个TForM

    八 在DLL中建立一个TMDIChildForM

    九 示例:

    十 Delphi制作的Dll与其他语言的混合编程中常遇问题:

    十一 相关资料

     

    一 Dll的制作一般分为以下几步:

    1 在一个DLL工程里写一个过程或函数

    2 写一个Exports关键字,在其下写过程的名称。不用写参数和调用后缀。

    示例

    加个Func进来: 

    FunctionMyMax ( X , Y : integer ) : integer ; stdcall ; 

    begin 

    if X > Ythen 

    Result :=X 

    else 

    Result := Y; 

    end ; 

    //切记:Library的名字大小写没关系,可是DLL-Func的大小写就有关系了。 

    // 在DLL-Func-Name写成MyMax与myMAX是不同的。如果写错了,立即 

    //的结果是你叫用到此DLL的AP根本开不起来。 

    //参数的大小写就没关系了。甚至不必同名。如原型中是(X,Y:integer)但引 

    //用时写成(A,B:integer),那是没关系的。 

    //切记:要再加个stdcall。书上讲,如果你是用delphi写DLL,且希望不仅给 

    //delphi-AP也希望BCB/VC-AP等使用的话,那你最好加个Stdcall ; 的指示 

    //参数型态:delphi有很多种它自己的变量型态,这些当然不是DLL所喜欢的 

    //,Windows/DLL的母语应该是C。所以如果要传进传出DLL的参数,我们 

    //尽可能照规矩来用。这两者写起来,后者会麻烦不少。如果你对C不熟 

    //的话,那也没关系。我们以后再讲。 

    将这些可共享的Func送出DLL,让外界﹝就是你的delphi-AP啦﹞使用:光如此,你的AP还不能用到这些,你还要加个Exports才行。 

    代码: 

    {$R*.RES} 

    exports 

    MyMax ; 

    begin 

    end.

     

     

    二 参数传递

    1 参数类型最好与windowC++的参数类型一致。不要用DELPHI的数据类型。

    2 最好有返回值[即使是一个过程],来报出调用成功或失败,或状态。成功或失败的返回值最好为1[成功]或0[失败].一句话,与windowsc++兼容。

    用stdcall声明后缀

    4 最好大小写敏感。

    5 无须用far调用后缀,那只是为了与windows16位程序兼容。

     

    三 DLL的初始化和退出清理[如果需要初始化和退出清理]

    1DLLProc[SysUtils单元的一个Pointer]是DLL的入口。在此你可用你的函数替换了它的入口。但你的函数必须符合以下要求[其实就是一个回调函数]。如下:

    procedureDllEnterPoint(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组件,就UsesActivex,在初始化时写一句CoInitialize (nil);

    3 在退出时一定保证DcomConnection.Connected:= False,并且数据集已关闭。否则报地址错。

     

    四 全局变量的使用

    在widnows32位程序中,两个应用程序的地址空间是相互没有联系的。虽然DLL在内存中是一份,但变量是在各进程的地址空间中,因此你不能借助dll的全局变量来达到两个应用程序间的数据传递,除非你用内存映像文件。

     

    五 调用静态载入

    1 客户端函数声名:

    1)大小写敏感。

    2)与DLL中的声明一样。

       如: showform(form:Tform);Far;external'yproject_dll.dll';

    3)调用时传过去的参数类型最好也与windowsc++一样。

    4)调用时DLL必须在windows搜索路径中,顺序是:当前目录;Path路径;windows;widows\system;windows\ssystem32;

     

    六 调用动态载入

    1 建立一种过程类型(或者是一个Function)[如果你对过程类型的变量只是一个指针的本质清楚的话,你就知道是怎么回事了]。如:

    type

       mypointer=procedure(form:Tform);Far;external;

       //mypointer=function(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' {

     

    };

     

    {$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;

     

    interface

     

    uses

      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编译成二进制代码也如此。  

     

    DELPHI中编写DLL时,如果DLL有创建ADO对象要被调用函数开始处写:CoInitialize(nil);

    结束时写:CoUninitialize;

    如要返回字符串要用PChar,最好用PCharoutvar方式返回,PChar的内存分配和释放在调用函数处理:GetMem(p,Size);FreeMem(p);

     

    procedure CommonDLL(AHnd: THandle; //AApp: TApplication;

      ADllFileName: PChar;

      AConnStr: PChar; AUserID:integer; ABillTypeID: integer);

    var

      LPtr:PLongint;

      strCon: widestring;

      strDllFileName: string;

    begin

      CoInitialize(nil);

      strCon := StrPas(AConnstr);

      strDllFileName :=StrPas(ADllFileName);

      try

        Application.Handle :=AHnd;

        Screen := AScr;

        LPtr :=@Application.MainForm;

        LPtr^ :=Longint(AApp.MainForm);

     

      finally

      end;

      CoUninitialize;

    end;

     

     

    在加载DLL时要保存原来的句柄,退出DLL时还原句柄:

    var

      OldHnd: THandle;

    //  OldApp: TApplication;

      OldScr: TScreen;

     

    procedure InitDll(dWseason: DWORD);

    begin

      case dWseason of

        DLL_PROCESS_ATTACH:

          begin

            OldHnd :=Application.Handle;

    //        OldApp:= Application;

            OldScr :=Screen;

          end;

        DLL_PROCESS_DETACH:

          begin

           Application.Handle := OldHnd;

    //        OldApp:= GApplication;

            OldScr :=GScreen;

          end;

      end;

    end;

     

    begin

      DllProc := @InitDll;

      InitDll(DLL_PROCESS_ATTACH);

    end.

     

     

    DLL被用函数中有创建窗体对象,一定要记得传Application或者Application.Handle

    DLL中的窗体一般要用FormClass.Create(application)来创建比较好。

     

    我见过有DLL会传Screen对象到DLL,这个我不没具体了解为什么,也希望有知道朋友告诉我有一下传Screen对象有什么作用,有什么优缺点。

     

    传Screen是为了使主程序主窗体的MDIChildCount正常增加,

    否则,不管打开多少DLL中的MDIChild窗体,MDIChildCount都不会增加。

     

    第一章 为什么要使用动态链接库(DLL top 

    提起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

     

    注意:在这里笔者假定读者使用的是Delphi 3或Delphi 4开场白说了那么多,总该言归正传了。编写DLL其实也不是一件十分困难的事,只是要注意一些事项就够了。为便于说明,我们先举一个例子。 

     

    library Delphi; 

     

    uses 

    SysUtils, 

    Classes;

     

    functionTestDll(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。从Delphi 3以后将这个参数变为了stdcall,目的是为了使用标准的Win32参数传递技术来代替优化的register参数。忘记使用stdcall参数是常见的错误,这个错误不会影响DLL的编译和生成,但当调用这个DLL时会发生很严重的错误,导致操作系统的死锁。原因是register参数是Delphi的默认参数。 

     

    二、所写的函数和过程应该用exports语句声明为外部函数。 

    正如大家看到的,TestDll函数被声明为一个外部函数。这样做可以使该函数在外部就能看到,具体方法是单激鼠标右键用“快速查看(Quick View)”功能查看该DLL文件。(如果没有“快速查看”选项可以从Windows CD上安装。)TestDll函数会出现在Export Table栏中。另一个很充分的理由是,如果不这样声明,我们编写的函数将不能被调用,这是大家都不愿看到的。 

     

    三、当使用了长字符串类型的参数、变量时要引用ShareMem。 

    Delphi中的string类型很强大,我们知道普通的字符串长度最大为256个字符,但Delphi中string类型在默认情况下长度可以达到2G。(对,您没有看错,确实是两千兆。)这时,如果您坚持要使用string类型的参数、变量甚至是记录信息时,就要引用ShareMem单元,而且必须是第一个引用的。既在uses语句后是第一个引用的单元。如下例: 

    uses 

    ShareMem, 

    SysUtils, 

    Classes; 

    还有一点,在您的工程文件(*.dpr)中而不是单元文件(*.pas)中也要做同样的工作,这一点Delphi自带的帮助文件没有说清楚,造成了很多误会。不这样做的话,您很有可能付出死机的代价。避免使用string类型的方法是将string类型的参数、变量等声明为Pchar或ShortString(如:s:string[10])类型。同样的问题会出现在当您使用了动态数组时,解决的方法同上所述。 

     

    第三章 在Delphi中静态调用DLL

     

    调用一个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:\,则我们可将上面的引用语句写为external’C:\Delphi.dll’。注意文件的后缀.dll必须写上。 

     

    三、不能从DLL中调用全局变量。 

    如果我们在DLL中声明了某种全局变量,如:var s:byte 。这样在DLL中s这个全局变量是可以正常使用的,但s不能被调用程序使用,既s不能作为全局变量传递给调用程序。不过在调用程序中声明的变量可以作为参数传递给DLL。 

     

    四、被调用的DLL必须存在。 

    这一点很重要,使用静态调用方法时要求所调用的DLL文件以及要调用的函数或过程等等必须存在。如果不存在或指定的路径和文件名不正确的话,运行主程序时系统会提示“启动程序时出错”或“找不到*.dll文件”等运行错误。 

     

    第四章 在Delphi中动态调用DLL

     

    动态调用DLL相对复杂很多,但非常灵活。为了全面的说明该问题,这次我们举一个调用由C++编写的DLL的例子。首先在C++中编译下面的DLL源程序。 

     

    #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:Thandle; 

    Tf:TIntFunc; 

    Tp:TFarProc; 

    begin 

    Th:=LoadLibrary(’Cpp.dll’); {装载DLL} 

    if Th>0 then 

    try 

    Tp:=GetProcAddress(Th,PChar(TestC)); 

    ifTp<>nil 

    thenbegin 

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

     

    第五章 使用DLL的实用技巧

     

    一、编写技巧。 

    1 、为了保证DLL的正确性,可先编写成普通的应用程序的一部分,调试无误后再从主程序中分离出来,编译成DLL 

    2 、为了保证DLL的通用性,应该在自己编写的DLL中杜绝出现可视化控件的名称,如:Edit1.Text中的Edit1名称;或者自定义非Windows定义的类型,如某种记录。 

    3 、为便于调试,每个函数和过程应该尽可能短小精悍,并配合具体详细的注释。 

    、应多利用try-finally来处理可能出现的错误和异常,注意这时要引用SysUtils单元。 

    5 、尽可能少引用单元以减小DLL的大小,特别是不要引用可视化单元,如Dialogs单元。例如一般情况下,我们可以不引用Classes单元,这样可使编译后的DLL减小大约16Kb 

    二、调用技巧。 

    1 、在用静态方法时,可以给被调用的函数或过程更名。在前面提到的C++编写的DLL例子中,如果去掉extern ”C”语句,C++会编译出一些奇怪的函数名,原来的TestC函数会被命名为@TestC$s等等可笑的怪名字,这是由于C++采用了C++ namemangling技术。这个函数名在Delphi中是非法的,我们可以这样解决这个问题: 

    改写引用函数为 

    functionTestC(i:integer):integer;stdcall; 

    external Cpp.dll;name@TestC$s

    其中name的作用就是重命名。 

    2 、可把我们编写的DLL放到Windows目录下或者Windows\system目录下。这样做可以在external语句中或LoadLibrary语句中不写路径而只写DLL的名称。但这样做有些不妥,这两个目录下有大量重要的系统DLL,如果您编的DLL与它们重名的话其后果简直不堪设想,况且您的编程技术还不至于达到将自己编写的DLL放到系统目录中的地步吧! 

    三、调试技巧。 

    1 、我们知道DLL在编写时是不能运行和单步调试的。有一个办法可以,那就是在Run|parameters菜单中设置一个宿主程序。在Local页的HostApplication栏中添上宿主程序的名字就可进行单步调试、断点观察和运行了。 

    2 、添加DLL的版本信息。开场白中提到了版本信息对于DLL是很重要的,如果包含了版本信息,DLL的大小会增加2Kb。增加这么一点空间是值得的。很不幸我们如果直接使用Project|options菜单中Version选项是不行的,这一点Delphi的帮助文件中没有提到,经笔者研究发现,只要加一行代码就可以了。如下例: 

     

    library Delphi; 

    uses 

    SysUtils, 

    Classes; 

    {$R *.RES} 

    //注意,上面这行代码必须加在这个位置 

    function TestDll(i:integer):integer;stdcall; 

    begin 

    Result:=i; 

    end; 

    exports 

    TestDll; 

    begin 

    end. 

    3 、为了避免与别的DLL重名,在给自己编写的DLL起名字的时候最好采用字符数字和下划线混合的方式。如:jl_try16.dll。 

     

    4 、如果您原来在Delphi 1或Delphi 2中已经编译了某些DLL的话,您原来编译的DLL是16位的。只要将源代码在新的Delphi 3或Delphi 4环境下重新编译,就可以得到32位的DLL了。 

     

    [后记]:除了上面介绍的DLL最常用的使用方法外,DLL还可以用于做资源的载体。例如,在Windows中更改图标就是使用的DLL中的资源。另外,熟练掌握了DLL的设计技术,对使用更为高级的OLECOM以及ActiveX编程都有很多益处。  

     

     2007-9-14 16:22:41   

    Delphi中如何调用DLL 

    马上想得到的使用说明有以下几点:

     

    1. 所需动态连结的 DLL 须置放在与执行档同一目录或Windows System 目录2. 确认 DLLexport 出来的函式的原型, 以目前的情况而言, 通常只拿得到 C语言的函数原型,这时要注意 C 与 object Pascal 相对应的型别, 如果需要, 在interface 一节定义所需的资料类别

     

    3. 在 implementation 节中宣告欲使用的函式, 语法大致如下:

     

    procedure ProcName(Argu...); far; external ’DLL档名’;

     

    index n;

     

    function FuncName(Argr...): DataType; far;

     

    external ’DLL档名’; index n;

     

    宣告时, indexn 如果不写, 便是参考资料中所谓 import by name 的方式, 此时, 由於需要从 DLL 的 name table 中找出这个函式, 因此, 连结执行速度比importby ordinal稍慢一些, 此外, 还有一种 by newname, 由於我没用过, 您可以查一参考资料, 大意是可以 import 後改用另一个程式命名呼叫这个函式

     

    4. 然後, 呼叫与使用就与一般的Delphi 没有两样5. 上述是直接写到呼叫DLL函式的程式单元中, 此外,也可以将DLL的呼叫宣告集中到一个程式单元(Import unit), Delphi 内附的 WinTypes,WinProcs是一个例子,

     

    您可以参考一下,同时观察一下 C 与 Pascal 互相对应的资料型态6. 除了上述的 static import 的方式, 另外有一种 dynamic import 的写法,先宣告一个程序类型(procedural-type),程式执行时, 以LoadLibrary() API Load进来後, 再以 GetProcAddress() API 取得函式的位址的方式来连结呼叫, 在ObjectPascal Language Guide P.132-133 有一个例子, 您可以参考看看

     

    如果要举个例子, 以下是从我以前的程式节录出来的片断:

     

    (* for CWindows 3.1 *)

     

    unit Ime31;

     

    interface

     

    uses

     

    SysUtils, WinTypes, WinProcs, Dialogs;

     

    type

     

    (* 必要的资料型态宣告 *)

     

    tDateNTime = record

     

    wYear, wMonth, wDay: word;

     

    wHour, wMin, wSec: word;

     

    end;

     

    TImePro = record

     

    hWndIme: HWnd; { IME handle }

     

    dtInstDate: tDateNTime; { Date and time of installation }

     

    wVersion: word; { the version of IME }

     

    szDescription: array[0..49] of byte; { Description of IME module}

     

    szName: array[0..79] of byte; { Module name of the IME }

     

    szOptions: array[0..29] of byte; { options of IME at startup}

     

    fEnable: boolean; { IME status; True=activated,False=deactivated }

     

    end;

     

    pTImePro = ^TImePro;

     

    function SetIme(const sImeFileName: string): boolean; far;

     

    implementation

     

    (* begin 呼叫 winnls.dll export 函数的宣告 *)

     

    function ImpSetIme(hWndIme: HWND; lpImePro: pTImePro):boolean;far; external ’winnls.dll’;

     

    (* end 呼叫 winnls.dll export 函数的宣告 *)

     

    (* -------------------------------------------------- *)

     

    (* SetIme(const sImeFileName: string): boolean;

     

    (* ======

     

    (* 切换到某一特定的输入法

     

    (*

     

    (* 传入引数:

     

    (* sImeFileName: 输入法 IME 档名, 例: phon.ime;

     

    (* 空字串: 英数输入法

     

    (*

     

    (* 传回值:

     

    (* True: 切换成功

     

    (* False: 失败

     

    (* -------------------------------------------------- *)

     

    function SetIme(const sImeFileName: string): boolean;

     

    var

     

    pImePro: pTImePro;

     

    begin

     

    Result := False;

     

    if MaxAvail < SizeOf(TImePro) then

     

    begin

     

    MessageDlg(’记忆体不足’, mtWarning, [mbOk], 0);

     

    Exit;

     

    end

     

    else

     

    begin

     

    New(pImePro);

     

    try

     

    if sImeFileName = ’’ then (* 空字串, 还原到英数输入法 *)

     

    pImePro^.szName[0] := 0

     

    else

     

    StrPCopy(@pImePro^.szName, sImeFileName);

     

    Result := ImpSetIme(0, pImePro); (* 呼叫 ImpSetIme*)

     

    finally

     

    Dispose(pImePro);

     

    end; { of try }

     

    end;

     

    end; { of SetIme }

     

    end.

     

     

    DELPHI 中DLL开发常见问题的讨论:

    http://www.delphibbs.com/delphibbs/DispQ.asp?LID=3685176 

     

     

     2007-4-6 19:25:51    如要返回字符串要用PChar,最好用PCharoutvar方式返回,PChar的内存分配和释放在调用函数处理:GetMem(p, Size);    FreeMem(p);

     

    而在被调用函数写的方式应该是:

     

     

    procedure GetStr(var Pstr: PChar);

    var

      str: string

    begin

      str := 'return string';

      strCopy(PStr,PChar(str));

    end;

     

    调用函数写法:

    TGetStr= procedure(var Pstr: PChar);

     

    funtion GetDllStr: string

    var

      DllHnd: THandle;

      GetStr: TGetStr;

      Str: PChar;

      strPath: string;

    begin

      AHaveWhere := 0;

      DllHnd := LoadLibrary(PChar('testdll'));

      try

        if (DllHnd <> 0) then

        begin

          @GetStr :=GetProcAddress(DllHnd, 'GetStr');

          if (@GetStr<>nil) then

          begin

            GetMem(Str, 1024);

            try

              GetStr(Filter);

              result := StrPas(Filter);

            finally

              FreeMem(Str);

            end;

          end

          else

          begin

           application.MessageBox(PChar('DLL加载出错,DLL可能不存在!'), PChar('错误'),

              MB_ICONWARNING or MB_OK);

          end;

        end;

      finally

        FreeLibrary(DllHnd);

      end;  

     

    展开全文
  • 利用C++制作dll并调用dll

    万次阅读 2016-05-26 15:58:40
    Abstract:本文讲解如何利用c++制作dll并调用dll,用一个简单的加法函数来作为演示,并给出图形界面。1. 打开vs(我用的是vs2010),然后点击文件—>文件—>新建—>项目,选择Win32控制台应用程序。并输入你想建立的...

    Abstract:本文讲解如何利用c++制作dll并调用dll,用一个简单的加法函数来作为演示,并给出图形界面。

    1. 打开vs(我用的是vs2010),然后点击文件—>文件—>新建—>项目,选择Win32控制台应用程序。并输入你想建立的vs工程名字和路径(就命名为addfun)。然后点击下一步,应用程序类型选择DLL(D)这一项。然后点击完成。就会出现下图。

    这里写图片描述

    2.里面有一个源文件addfun.cpp,我们可以编辑里面的内容了。里面主要是我们想生成的函数。另外我们需要建立一个与addfun.cpp相对应的头文件addfun.h。直接右键到头文件—>添加—>新建项—>选头文件,命名为addfun.h。头文件就我们想生成的dll的函数名了。
    3.现在我们开始编辑addfun.cpp文件了。如下所示:

    // abc.cpp : 定义 DLL 应用程序的导出函数。
    //
    
    #include "stdafx.h"
    #include "addfun.h"
    
    int add(int a,int b)
    {
        return a+b;
    }

    4.然后我们再编辑addfun.h,如下所示:

    #include "stdafx.h"
    
    
    extern "C"
    {
        _declspec(dllexport) int add(int a,int b);
        typedef int (* ApiAdd)(int,int);
    }

    按照上面的格式我们编译就能生成我们想要的dll的api。

    通过以上4步,我们可以在工程的Debug目录下找到addfun.dll和addfun.lib。

    下面我们就开始介绍怎么来调用一个dll。
    1.建立一个c++工程,就建立一个空文件就可以了。我们需要的就是addfun.dll和头文件。这个都可以拷到我们的新工程下。直接右键头文件,添加现有项把addfun.h添加过来。添加过来是这样的:

    #include "stdafx.h"
    
    
    extern "C"
    {
        _declspec(dllexport) int add(int a,int b);
        typedef int (* ApiAdd)(int,int);
    }

    但是现在我们需要修改它,不要再生产dll,我们需要把它改为

    #include "stdafx.h"
    
    
    extern "C"
    {
        int add(int a,int b);
        typedef int (* ApiAdd)(int,int);
    }

    上面代码和下面代码区别在于少了_declspec(dllexport),这个是生成dll的,我们现在不需要了。

    2.下面展示下如何在主函数中加载并调用dll:

    // addtest.cpp : 定义控制台应用程序的入口点。
    //
    
    #include "stdafx.h"
    #include <Windows.h>
    #include "addfun.h"
    #include<iostream>
    #include<stdlib.h>
    using namespace std;
    int _tmain(int argc, _TCHAR* argv[])
    {
        int a=2,b=1,c=0;
        HINSTANCE hDllInst = LoadLibrary("addfun.dll");
        ApiAdd myfun=0;
        myfun = (ApiAdd)GetProcAddress(hDllInst,"add");
    // youFuntionName 在DLL中声明的函数名
        if(myfun)
        {
            c=myfun(a,b);
        }
    
        FreeLibrary(hDllInst);
        cout<<c<<endl;
        system("pause");
        return 0;
    }

    这是程序这一行会有红色波浪 HINSTANCE hDllInst = LoadLibrary(“addfun.dll”);
    这样修改:右键工程(addtest)—>属性—>常规—>字符集。这里写图片描述
    好的,现在就算把程序调用成功了。下面就是结果了:

    这里写图片描述

    所以制作dll也不是很难。
    其他所有函数都可以这样来做,我们只需要把我们想打包的函数在头文件中指明,然后想使用这些函数的时候,头文件包含进去并把_declspec(dllexport)删除就可以了。然后load相应的dll就可以了。

    展开全文
  • 提取EXE图标制作DLL工具 提取EXE图标制作DLL工具 提取EXE图标制作DLL工具
  • VS2019制作DLL文件

    千次阅读 2019-10-30 13:34:11
    用VS2019制作DLL文件的一般步骤很简单: 1创建新项目 2找到动态链接库DLL项目 3创建 4.分别在源文件和头文件添加相关函数f.cpp,f.h 第4步中,f.cpp没有主函数的各种自己的函数,f.h文件中是对应的头文件,不过...
  • 教你用VB制作DLL

    2013-06-15 23:38:27
    教你用VB制作DLL,从制作到使用,再到把DLL写入系统全套教程[必须解压才能看]</H1>
  • DLL图标文件制作工具IconJack32---提取EXE图标制作DLL工具
  • C#制作DLL

    2019-09-23 23:52:41
    因此需要自己制作.net DLL文件。以下为操作步骤: 步骤1:新建C#项目 步骤2:查找对应控件的命名空间及程序集 步骤4:添加程序集引用 步骤5:使用命名空间,继承控件属性 步骤6:生成项目,在项目...
  • 摘要:VB源码,控件组件,DLL VB做DLL并引用DLL的源码实例,新建个ActiveX DLL工程,修改类名,将Class1改成thedll,(也可以改成其他名字,在exe中要注意声明类名就是你改的那个),写DLL代码(代码和类一样,有属性和...
  • 适合初学者了解并自动动手制作DLL文件。 DLL即动态链接库(Dynamic Link Library),是由可被其它程序调用的函数集合组成的可执行文件模块。DLL不是应用程序的组成部分,而是运行时链接到应用程序中。
  • C++ 制作dll文件并调用

    2019-07-07 09:11:23
    制作dll 此项目的名字是nccnew,其中的想要调用的函数是add_test,就是一个简单的加法函数。 #include <iostream> extern "C" __declspec(dllexport) int add_test(int a, int b); int main() { int d; d ...
  • RadASM制作DLL文件并注入一个位图启动 有几个方法将位图启动注入一个二进制文件。不幸的是非常不易。本教程中显示的方式是我遇到的最简单的,虽然可能有更简单的选择。问题是位图是一种资源,为此在一个现有的二进制...
  • DLL:VS2005使用C++制作dll文件

    千次阅读 2012-07-25 16:28:43
    c++制作dll文件 dll文件的c++制作 1、首先用vs2005建立一个c++的dll动态链接库文件,这时, // DllTest.cpp : 定义 DLL 应用程序的入口点。 // #include "stdafx.h" //#include "DllTest.h" #ifdef _...
  • 记录c++制作dll并调用

    2018-11-09 10:57:02
    记录c++制作dll并调用 打开vs(我用的是vs2017),然后点击文件—&gt;文件—&gt;新建—&gt;项目,选择windows桌面向导。并输入你想建立的vs工程名字和路径(就命名为addfun)。然后点击下一步,应用程序...
  • 接口模式的制作DLL方法向导 释放的目标文件夹就是 DLL工程文件夹 释放完成后,输入DLL的文件名 优点: 1、DLL只导出固定的两个方法,获取接口和释放接口函数。 象普通类一样使用接口的方法; 2、释放非常干净,可...
  • 近来学习制作Dll文件,看了几个视频教程,看了网上的例子,看了msdn上的例子。现在做个总结,以便来日回顾,同时也希望以大家相互交流学习。注意1:用 method 1 named "Using Load - Time Dynamic Linking"...
  • delphi制作 dll实例

    2013-11-07 20:58:02
    dll内放置改写,读取注册表的function function regkeyexists(x:shortstring):boolean;stdcall;External 'reg.dll' function regkeydelete(x:shortstring):boolean;stdcall;External 'reg.dll' function ...
  • Delphi制作DLL(收集)

    千次阅读 2012-02-03 10:25:54
    Delphi制作DLL 一 Dll的制作一般步骤 二 参数传递 三 DLL的初始化和退出清理[如果需要初始化和退出清理] 四 全局变量的使用 五 调用静态载入 六 调用动态载入 七 在DLL建立一个TForM 八 在DLL中建立一个...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 42,834
精华内容 17,133
关键字:

如何制作dll