• Dephi 之DLL技巧汇集


     

    参考文章:Delphi环境中编写调用DLL的方法和技巧http://www.chinaaspx.com/club/topic_11_3941.htm

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

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

    四、被调用的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了

    参考文章:在Delphi与C++之间实现函数与对象共享  http://www.zahui.com/html/2/4202.htm

    1.C++共享Delphi对象

    要实现从C++调用Delphi对象,首先要在Delphi单元的接口部分以及C++的头文件中说明需要共享的对象的接口,在对象接口中定义该对象包含哪些属性与方法,并说明可供共享的部分。对象的共享,关键在于方法的共享。在Delphi语言中,要使一个对象可以被共享,可以把它说明为两个接口部分,暂称为"共享接口"与"实现接口"。其中共享接口指明对象中哪些方法可被另一种语言所共享;实现接口则继承共享接口,并且在单元实现部分针对实现接口中的方法定义具体的实现。要定义一个可供C++共享的Delphi对象,共享接口的说明应注意:

    在Delphi程序里,要共享的方法必须被说明为抽象(abstract),而且虚拟(virtual );

    在C++程序里,必须用关键字"virtual"及"=0"后缀,把从Delphi共享的方法说明成"pure virtual";

    共享的对象方法必须在两种语言里都被说明成相同的调用方式,通常使用标准系统调用方式(stdcall)。

    下面,举例说明这些规则,假设有这样的一个Delphi对象:
    TTestObject=class
    procedure Proc1(x:integer);
    function Func1(x:integer):PChar;
    procedure Proc2;
    function Func2:integer;
    end;

    如果C++程序需要共享其中的方法Proc1、Func1,可把上述说明修改成以下形式:
    STestObject=class
    procedure Proc1(x:integer); virtual; abstract; stdcall;
    function Func1(x:integer); virtual; abstract; stdcall;
    end;
    TTestObject=class(STestObject)
    procedure Proc1(x:integer);
    fuction Func1(x:integer):PChar;
    procedure Proc2;
    fuction Func2:integer;
    end;

    在C++程序中做如下对象原型说明:
    class STestObject {
    virtual void Proc1(int x)=0;
    virtual char *Func1(int x)=0;
    };

    为了能在C++中成功地访问Delphi定义的类, Delphi接口说明时必须包含一个可共享的"制造函数(Factory Function)"CreateTestObject,该制造函数可被定义在动态链接库或目标文件(.OBJ)中,例如:

    Library TestLib;
    exports CreateTestObject;
    function CreateTestObject:STestObject; stdcall;
    begin
    Result:=TTestObject.Create;
    end;

    end.

    经过这样的处理,现在可在C++程序中使用这个由Delphi定义的对象,调用方式如下:
    extern "C" STestObject stdcall *CreateTestObject();
    void UseTestObject(void) {
    STestObject *theTestObject=CreateTestObject();
    theTestObject->Proc1(10);
    Char *str=theTestObject->Func1(0);
    }

    当调用制造函数CreateTestObject时,实际上已经在Delphi一侧占用了一个对象实例的空间,C++程序在针对该对象的所有处理完成后必须考虑释放这一空间,具体的实现可在Delphi中定义一个类,如上述Proc1的共享方法Free,以此来完成这一任务:
    STestObject=class
    procedure Proc1(x:integer); virtual; abstract; stdcall;
    function Func1(x:integer); virtual; abstract; stdcall;
    procedure Free; virtual; abstract; stdcall;
    end;

    implementation

    procedure TTestObject.Free;
    begin

    end;

    end.

    2.Delphi共享C++对象
    通常,程序员会考虑使用Delphi来编制用户界面,所以Delphi代码调用C++代码似乎显得更加实际些。其实,Delphi共享C++对象的实现方法与上述C++共享Delphi对象非常相似。用同样的共享接口与实现接口说明方法来定义C++的类:

    class STestObjedt {
    virtual void Proc1(int x)=0;
    virtual char *Func1(int x)=0;
    };
    class TTestObjedt :public STestObject {
    void Proc1(int x);
    char *Func1(int x);
    void Proc2();
    int Func2();
    void Free();
    };

    然后实现这些方法。同样地,C++对象需要一个与之对应的制造函数,这里以DLL为例
    STestObject stdcall export *CreateTestObject() {
    return (STestObject *) new TTestObject.Create;
    }

    Delphi代码可以通过调用制造函数CreateTestObject,很容易地在C++中创建实例,获得指向该实例的指针值,并以这个指针值来调用对象中的共享方法。当然,在进行完该对象的相关处理后,千万不要忘了调用Free释放占用的空间。
    张维

    摘要:Delphi以其独特的面向控件的开发方式、强大的数据库功能以及快速的编译技术,使得它自发布起即格外引人注意。随着Delphi 5提供丰富的Internet应用,Delphi日益成为最重要的软件开发工具之一,它吸引了许多原Visual Basic、Foxpro、dBase甚至C++的程序员,而这些程序员使用Delphi时需要解决的一个重要问题就是怎样利用他们原有的代码。本文将介绍Delphi与C++程序集成的方法,包括:
    Delphi与C++之间函数的共享;
    代码的静态链接和动态链接;
    对象的共享。
    函数的共享

    在Delphi中调用C++函数与C++调用Delphi函数相当直接,需要注意的是,Delphi 1默认的函数调用方式是Pascal方式,Delphi 4、Delphi 5的默认方式则是优化的cdecl调用方式,即register方式。要在C++与Delphi程序之间实现函数共享,除非有充分的原因,否则应该使用标准系统调用方式,即stdcall方式。为了使C++编译器不将函数标记为"mang led",使Delphi编译器误认为函数是采用cdecl调用方式,应该在C++代码中,以extern "C "说明被共享的函数,如下例所示:

    原型说明:
    在C++中:
    extern "C" int _stdcall TestFunc();
    在Delphi中:
    function TestFunc:integer; stdcall;

    调用语法:
    在C++中:
    int i=TestFunc();
    在Delphi中:
    var i:integer;

    begin

    i:=TestFunc;

    end;

    共享函数的参数必须是两种语言都支持的变量类

    参考文章:DELPHI 中动态链接库的使用 http://blog.csdn.net/feifei1018/admin/EditPosts.aspx

    在DELPHI中,有两种方法可用于调用一个储存在DLL(动态链接库)中的过程。 

    一、 调用方法 

    1、 静态调用或显式装载使用一个外部声明子句,使DLL在应用程序开始执行前即被装入。例如: 

    Function instring (sourcestr: Pchar ;
    check: char): integer; far; external ‘ demostr’
    这种方式要在单元的interface 部分用external 指示字列出要从DLL中调用的例程。Far 指令表明可以被其他段,例如其他单元调用的子例程。所有在单元接口中声明的子例程在缺省情况下都是Far类型的,其相反的指令是near。 

    如果external 后什么也不跟,必须用 {$ L } 编译指令预先指定一个DLL名字,如: 

    { $ L Mydlls.dll }
    Procedure setstring(var str: string) ;
    stdcall ; external
    但是使用静态调用方法时,程序无法在运行时间里决定DLL的调用。在DELPHI中使用DLL时,例程的标识符必须与DLL中相应输出例程的标识符完全一致(尽管DELPHI本身大小写不敏感)。 

    2、 动态调用或隐式装入 

    使用WINDOWS API 函数 Loadlibrary 和GetprocAddress可以实现在运行时间里的动态装载DLL,并调用其中的过程。 

    例如: 

          Type TMyProc=Procedure (Param:Pchar ) ;Stdcall;
          Var MyProc: TMyproc;
           MyHandle:THandle;
           MyHandle:=LoadLibrary (‘Mydll’) ;
           If MyHandle<  =0 then
            Raise Exception.Create
            ( ‘动态链接库调用失败,错误代码
            是:’+Inttostr(Getlasterror))
            else 
             @MyProc:=GetProcAddress(MyHandle,’demoproc’);
           if not Assigned(MyProc) then
            Raise Exception.Create('GetProcAddress 
            调用失败,错误代码
               是:’+inttostr(getlasterror))
            else MyProc(Pchar(‘a string’));
            Freelibrary(Myhandle); // 卸载DLL
    二、 调用方式 

    1、 通过过程、函数名; 

    2、 通过过程、函数别名; 

    3、 通过过程、函数的顺序号 

    例:Function Getstring : string ; stdcall ; external ‘Mydlls.dll’ name ‘Mygetstr’name 子句指定函数名Getstring 改为Mygetstr,当程序调用这个例程时,使用Mygetstr这个名字;Function Getstring : string ; stdcall ; external ‘Mydlls.dll’ index 5 Index 子句通过索引号引入例程可以减少DLL的加载时间。 

    三、 调用约定 

    调用约定,是指调用例程时参数的传递顺序。DELPHI中DLL支持的调用约定有: 

    调用约定 参数传递顺序
    Register 从左到右
    Pascal 从左到右
    Stdcall 从右到左
    Cdecl 从右到左
    Safecall 从右到左
    使用Stdcall 方式,能保证不同语言写的DLL的兼容性,同时它也是WINDOWS API的约定方式;Delphi 3。0、4。0的默认调用方式为Register ;Cdecl是采用 C/C++的调用约定,适用于DLL是由C++语言编写的;Safecall 是适合于声明OLE对象中的方法。 

    四、 DLL中的变量和段 

    一个DLL声明的任何变量都为自己私有 ,调用它的模块不能直接使用它定义的变量。要使用时必须通过过程或函数界面才能完成,对DLL来说,它永远都没有机会使用调用它的模块中的声明的变量。一个DLL没有自己的SS(堆栈段),它使用调用它的应用程序的堆栈。因此在DLL中的过程、函数不要假定DS=SS(DS为数据段)。 

  • 相关阅读:
    vijosP1360-八数码问题
    android——卡片式布局
    android ——悬浮按钮及可交互提示
    android ——滑动菜单
    android ——Toolbar
    android ——多线程编程
    android ——调用摄像头拍照和相册
    android ——通知管理
    android——实现跨程序访问数据
    android——SQLite数据库存储(操作)
  • 原文地址:https://www.cnblogs.com/ctoroad/p/249267.html
Copyright © 2020-2023  润新知