热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Delphi的DLL技巧汇集

Delphi的DLL技巧汇集调用一个DLL比写一个DLL要容易一些。首先给大家介绍的是静态调用方法,稍后将介绍动态调用方法,并就两种方法做一个比较。同样的,我们先举一个静态调用的例子。
    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 文件 等运行错误。
  
  编写技巧
  
   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;    
推荐阅读
  • Java中包装类的设计原因以及操作方法
    本文主要介绍了Java中设计包装类的原因以及操作方法。在Java中,除了对象类型,还有八大基本类型,为了将基本类型转换成对象,Java引入了包装类。文章通过介绍包装类的定义和实现,解答了为什么需要包装类的问题,并提供了简单易用的操作方法。通过本文的学习,读者可以更好地理解和应用Java中的包装类。 ... [详细]
  • C++字符字符串处理及字符集编码方案
    本文介绍了C++中字符字符串处理的问题,并详细解释了字符集编码方案,包括UNICODE、Windows apps采用的UTF-16编码、ASCII、SBCS和DBCS编码方案。同时说明了ANSI C标准和Windows中的字符/字符串数据类型实现。文章还提到了在编译时需要定义UNICODE宏以支持unicode编码,否则将使用windows code page编译。最后,给出了相关的头文件和数据类型定义。 ... [详细]
  • ejava,刘聪dejava
    本文目录一览:1、什么是Java?2、java ... [详细]
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • Android Studio Bumblebee | 2021.1.1(大黄蜂版本使用介绍)
    本文介绍了Android Studio Bumblebee | 2021.1.1(大黄蜂版本)的使用方法和相关知识,包括Gradle的介绍、设备管理器的配置、无线调试、新版本问题等内容。同时还提供了更新版本的下载地址和启动页面截图。 ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • Week04面向对象设计与继承学习总结及作业要求
    本文总结了Week04面向对象设计与继承的重要知识点,包括对象、类、封装性、静态属性、静态方法、重载、继承和多态等。同时,还介绍了私有构造函数在类外部无法被调用、static不能访问非静态属性以及该类实例可以共享类里的static属性等内容。此外,还提到了作业要求,包括讲述一个在网上商城购物或在班级博客进行学习的故事,并使用Markdown的加粗标记和语句块标记标注关键名词和动词。最后,还提到了参考资料中关于UML类图如何绘制的范例。 ... [详细]
  • 第一步:PyQt4Designer设计程序界面该部分设计类同VisvalStudio内的设计,改下各部件的objectName!设计 ... [详细]
  • AtonepointIhadlookedatimplementingaclasstemplateinC++thatwouldsupportanEnumthatwo ... [详细]
  • vb如何去掉最后的换行符?这是VB在读多行文件时出现的问题,最后行多了换行字符。可以用Left函数来取去除最后换行字符的文本。Left函数返回Variant(String),其中包 ... [详细]
  • Final关键字的含义及用法详解
    本文详细介绍了Java中final关键字的含义和用法。final关键字可以修饰非抽象类、非抽象类成员方法和变量。final类不能被继承,final类中的方法默认是final的。final方法不能被子类的方法覆盖,但可以被继承。final成员变量表示常量,只能被赋值一次,赋值后值不再改变。文章还讨论了final类和final方法的应用场景,以及使用final方法的两个原因:锁定方法防止修改和提高执行效率。 ... [详细]
  • 本文介绍了在Python3中如何使用选择文件对话框的格式打开和保存图片的方法。通过使用tkinter库中的filedialog模块的asksaveasfilename和askopenfilename函数,可以方便地选择要打开或保存的图片文件,并进行相关操作。具体的代码示例和操作步骤也被提供。 ... [详细]
  • 本文介绍了数据库的存储结构及其重要性,强调了关系数据库范例中将逻辑存储与物理存储分开的必要性。通过逻辑结构和物理结构的分离,可以实现对物理存储的重新组织和数据库的迁移,而应用程序不会察觉到任何更改。文章还展示了Oracle数据库的逻辑结构和物理结构,并介绍了表空间的概念和作用。 ... [详细]
  • Winform中实现连接Mysql8使用mysqldump实现备份表的数据
    场景Winform中连接Mysql8并查询表中数据进行显示:https:blog.csdn.netBADAO_LIUMANG_QIZHIarticledetails12039598 ... [详细]
  • 今天来法分析AlertDialogcancel()销毁窗口流程。前面文章说了其实activity和dialog都是通过window对象来管理视图的。所以我们可以从AlertDialog销毁过程来了 ... [详细]
author-avatar
滒娶伱
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有