Delphi的DLL技巧汇集

来源:互联网 发布:破解软件3687474扣 编辑:程序博客网 时间:2024/04/28 03:56
   
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 。这样在DLLs这个全局变量是可以正常使用的,但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 1Delphi 2中已经编译了某些DLL的话,您原来编译的DLL16位的。只要将源代码在新的Delphi 3Delphi 4环境下重新编译,就可以得到32位的DLL
  
  参考文章:在DelphiC++之间实现函数与对象共享 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++程序需要共享其中的方法Proc1Func1,可把上述说明修改成以下形式:
  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;
 
 
 
原创粉丝点击