Effective Delphi

条款1:不管怎么样,请让你的Project至少user一次SysUtils.pas单元

很多使用Delphi的人都对Delphi有着这样一个抱怨:Delphi虽然开发效率高,但是其编译出来的程序却是太大。使用Delphi5新建一个Project然后直接编译,程序的Size就已经达到了286KB,而如果把同样的程序放到Delphi7下面编译的话,那么其Size更是达到了360KB。正是由于这点,所以为Delphi编译生成的应用程序“减肥”便成为了几乎所有Delphi社区的一个保留性话题。

其间,大多数人都是使用可执行文件压缩工具(比如Aspack或者Upx等)来压缩Delphi所生成的可执行文件以达到“减肥”的目的,但是也有一些人,他们使用一种更为极端,但是更有效的方式来减少Delphi编译生成的可执行文件的Size,那就是抛弃VCL所提供的编程框架,而直接使用WIN32 SDK加上Object Pascal所提供的面向对象来能来进行程序的撰写。比如以下一段程序,使用Delphi5的编译器进行编译其大小只有16KB,而写一个基本的带窗口的Window程序其大小也不会超过25KB(以下这段程序使用Delphi3编译后会更小,其原因请见下述):

CODE:
program SmallPro;

uses

Windows;

{$R *.RES}

begin

MessageBox(0, 'Hello World!', 'Information', MB_OK);

end.

[Copy to clipboard]

请大家注意,以上程序是在project文件内直接编译,所以没有引用到其它的自定义单元。而所包含的Windows单元则只是为了调用MessageBox API函数而必须包含的。

这个编译出来的程序实在是太小了,小到它足以对那些熟悉Windows SDK方式编程,而又使用Delphi作为开发工具的人产生一定的诱惑力(我自己就算一个:->)。不知道这种方式是否同样也对你产生过诱惑力或者已经对你产生了诱惑力,如果是的话,那么先请听我一句忠告,“请为你的Project Uese上SysUtils.pas单元吧,否则你的程序将失去使用异常机制的能力,如果你不接这条忠告的话,你早晚会为你的行为任出代价。”

关于异常处理,各人的看法不同,有的人认为它是一种极美妙的错误处理方式:因为它能够使程序代码中处理错误部分的代码与实现逻辑部分的代码分离,使程序的源代码变得更优雅且撰写起来更方便和易读。而有人则认为使用异常机制来处理程序中的错误是不好的行为:因为一旦异常被触发,并且你未对其加控制的话,那么这个异常将导致应用程序终止,这种错误处理方式太过直接和粗鲁。但是,不管怎样,无庸置疑一点的是,你的程序代码可以不使用异常机制来处理错误,但是你却无法预计在你的代码当中所调用的各种库函数或者类是否使用或者支持异常机制,所以为了保证你程序的鲁棒性,即使你的代码不使用异常机制,那么你也应该在你代码的关键位置,加入异常处理的代码,以免你的代码所调用的其它代码或者操作系统抛出异常,导致程序意外的终止。下面便是一个简单的小例了:

CODE:
program SmallPro;

uses

Windows,

SysUtils;

{$R *.RES}

var

p: PChar;

begin

try

p := nil;

p^ := 'l';

except

MessageBox(0, 'Exception', 'Information', MB_OK);

end;

end.

[Copy to clipboard]

以上程序向地址空间0x00000000写一个字节的数据,在现在所有版本的Windows操作系统下面,这都将被系统视为非法操作,所以操作系统会抛出一个SHE异常,而Delphi的RTL系统会使你的程序能够栏截住这个异常并加以处理,如果你的程序没有处理这个异常的话,那么Delphi的RTL会弹出一个显示异常信息的对话框,并在你按下对话框的“确定”按钮后终止整个程序。我们上面的程序处理了这个异常,程序将在弹出MessageBox函数所显示的对话框后继续执行try…except.块后面的代码。

下面我们将上面的这个例子做一个很小的改动,将uses的SysUtils.pas单元去掉,然后再运行看看会出现什么样结果。

程序执行的结果和uses了SysUtils.pas单元的版本有着相当大的差异,程序只会显示一个如下图所示的:Runtime Error的对话框,然后便终止运行了,我们的异常处理块try..except则根本就没有起到作用。

(图1:运行时错误)

 

经过以上的测试,我想你已经能够明白,如果想让你的使用Delphi编译器所编译出来的程序能够支持异常机制的话,那么你就必须去在你的项目当中至少的包含的一次SysUtils.pas单元。写到此处,此条款应该可以说是功德圆满,但是我想我还是有必要带你简单的了解一下Delphi的整个异常处理机制,以便你能够对SysUtils.pas单元在整个Delphi异常机制中所占的地位有一个进一步的认识,并能够做到更好的使用它。

追根溯源,Delphi的编译器其实会向C/C++编译器一样为你的程序在链接时插入一段启动代码来使操作系统能够调用它,并启动整个应用程序(这个不是C/C++的main函数,如果你对这方面感兴趣的话,我建议你去读Jeffry Richter所著的《Programming Applications for Microsoft Windows Fourth Edition》,这本书的第4章对Processes的讲述中有相关的描述)。

对于以EXE形式存在的和以DLL形式存在的程序来说,Delphi为它们插入的启动代码的名称是不一样的,对于EXE型程序来说,Delphi会为你的程序插入其一个名称为_InitExe的过程,而对于DLL型程序来说,Delphi编译器会为你的程序插入一个名称为_InitLib的过程,你可以从SysInit.pas单元的源代码当中找到这两个过程的定义和实现。说到这里顺便提一句,System.pas和SysInit.pas两个Pascal单元是Delphi编译器在编译程序时默认包含的两个单元(你从来没有见到过哪一个程序uses过这两个单元吧)。而Delphi的每一个版本几乎都会对这两个单元进行扩展和修改,也正因为这个原因,所以在前面你看到的使用Delphi7编译的那个小程序的Size要比Delphi5编译出来的同样程序大的多。

在_InitExe过程的内部会调用一个名称为_StartExe的过程,而在这个_StartExe过程的内部中则会去调用在System.pas单元中定义的SetExceptionHandler函数来初始化整个Delphi的异常处理机制,在这个过程中设置的_ExceptionHandler过程则正是Delphi整个异常处理机制的核心处理过程。

在_ExceptionHandler会使用到System.pas单元中定义的一系列过程指针变量(比如ExceptProc,ExceptClsProc,ExceptObjProc等),这些过程指针变量都是Delphi整个异常机制当中必须的使用到的,而这些变量的初始化工作便是在SysUtils.pas单元中定义的InitExceptions单元中,InitExceptions变量会在SysUtils单元的initialization部分被调用,于是整个Delphi的异常处理机制便初始化完成。对于DLL型的程序,其异常处理过程的初始化部分与EXE型的程序一样,所以在这里就不再复述了。

好了在介绍了Delphi最核心的异常处理过程之后,我们再来介绍一下这些异常处理过程是如何被触发的。

当是一个异常被触发后,操作系统会最先拦截到这个异常。在操作系统拦截到这个异常后,它会马上调用.KiUserExceptionDispatcher函数(注1),这个函数是的Windows操作系统自身使用的异常处理函数,而在KiUserExceptionDispathcher函数调用的过程中,它会通过某种回调机制,最终去调用我们上面提到过的_ExceptionHandler过程,展开异常并处理之,如果没有找到如果被抛出异常所匹配的异常,那么则调用在SysUtils.pas中被赋值的ExceptHandler过程指针变量,抛出一个出现异常信息的话框,并在用户按下确定按钮之后终止程序。

这里面值得一的是,如果ExceptHandler过程指针变量的值为Nil,那么Delphi的RTL会去调用System.pas单元中定义的MapToRunError过程来做一个异常类型到运行时错误码的映射,并最终调用RunErrorAt过程在显示运行时错误对话框(如图1所示)终止整个程序的运行。

注1:我的操作系统是WIN2K所以KiUserExceptionDispatcher在ntdll当中,由于条件有限我没有在WIN98下做过类似的调试,不知道在WIN98下面是否也使用类似的方式来处理异常。另由于KiUserExceptionDispatcher函数微软未文档化的一个函数,所以我在这里不太方便对此函数的运作机制进行剖析(因为不同的操作系统中此函数的实现机制可能会不同),所以在这里还请您见谅。

转载于:https://www.cnblogs.com/temptation/archive/2006/04/25/384333.html

[转] 《完美程式设计指南》Effective Delphi相关推荐

  1. 游戏程式设计指南(HoHo篇)

    游戏程式设计指南(HoHo篇)Lesson 1 - 基础篇 序 游戏程式设计是令人向往而神秘的学科,其中涉及到非常广的程式技术,同时还需要对数理算法有相当的认识,可以说游戏程式设计是一条非常艰苦而漫长 ...

  2. rtl8811au黑苹果10.15_黑苹果10.15Catalina硬件选择+完美配置指南【接入智能家居】...

    macOS Catalina 10.15正式版 你甚至可将 iPad 作为第二个显示屏,扩展桌面空间. 现在,无论做什么,你将拥有远超以往的体验 1.硬件选择篇 装机硬件配置清单如下: UMX1 Pl ...

  3. lambda 函数完美使用指南

    来源:萝卜大杂烩 今天我们来学习 Python 中的 lambda 函数,并探讨使用它的优点和局限性 什么是 Python 中的 Lambda 函数 lambda 函数是一个匿名函数(即,没有名称定义 ...

  4. 汇编程序员之代码风格指南

    Style Guidelines for Assembly Language Programmers 汇编程序员之代码风格指南 作者:Randall Hyde   http://webster.cs. ...

  5. python没有联网_无网环境下的 Python 开发指南

    说起有关 Python 的指南,便一定得提到 K 神创建的『Python最佳实践指南』,英文名字是『The Hitchhiker's Guide to Python!』.毫不夸张地说,K 神创建的指南 ...

  6. David I 盛大之行及Delphi未来畅想

    4月15日,CodeGear的领袖人物David I拜访了盛大,并对Delphi的未来(下个版本)做了预告及展望,同时,也解答了我们压在心头多时的问题. 我在此把交流的记录发上来,让大家一起激动一下吧 ...

  7. DELPHI中自定义消息的发送和接收

    DELPHI中的消息处理机制 Delphi是Borland公司提供的一种全新的WINDOWS编程开发工具.由于它采用了具有弹性的和可重用的面向对象Pascal(object-orientedpasca ...

  8. Delphi Web前端开发教程(5):基于TMS WEB Core框架

    Delphi/Object Pascal的历史与现状 从技术上讲,Delphi 是一个由 Object Pascal 编程语言支持并为其提供开发环境的IDE,可以实现高生产力和快速应用程序开发的工具. ...

  9. 我手中的电子书书目清单(开始提供BT下载)

    13G电子书开始提供BT下载,下载说明及BT种子下载见我另一Blog网页:http://blog.csdn.net/yiyuan/archive/2005/10/16/504747.aspx 我手中的 ...

最新文章

  1. python编程课程上课有用吗-朋友圈里的编程课,是 Python 还是成功学?
  2. 忍不住也谈招聘应聘程序员的事
  3. windows文件服务器双机热备_遇到ZFS文件系统如此棘手的问题,这种办法简单又高效!...
  4. 【今晚七点半】:5G时代的云游戏还缺什么?
  5. redis复制_Redis复制
  6. pycharm无缘无故打开Nosetest
  7. c++的set_unexpected不起作用
  8. UrlRewrite 的配置和使用总结
  9. 清华郝景芳:中国教育还欠缺什么?如何弥补当下教育的不足? (公号回复“郝景芳”下载PDF典藏版)
  10. 日程提醒app android,手机里有提醒日程安排的软件吗?
  11. html5 canvas 绘制、移动方块及撤销操作
  12. AutoCAD快速入门(十三):倒角和圆角
  13. mysq根据首字母模糊检索,拼音首字母模糊查询名称
  14. java 文字串叠字检查_逆天叠字又来了!这次是五叠!六叠!八叠字!字字突破你的想象!...
  15. 计算机进制转换专项训练,计算机进制转换练习题.doc
  16. 人工智能基础(高中版)教材补充和资源分享
  17. 计算机色彩再现原理,清华大学出版社-图书详情-《计算机色彩原理及应用》
  18. 白话机器学习算法理论+实战番外篇之LightGBM
  19. 【Paper】A Review of Data-Driven Building Energy Consumption Prediction Studies
  20. 新建android项目

热门文章

  1. linux的同步与互斥
  2. muduo之Socket和SocketsOps
  3. 脚本重启nginx进程
  4. java中普通类、抽象类、接口的区别?
  5. 二值信号量和互斥锁到底有什么区别?
  6. java web六:tomcat其他小问题
  7. python二十四:python练习题
  8. linux 下一个 osw先从操作系统和标准脚本主动发起
  9. java.io.IOException: No space left on device
  10. HitFilm Pro 12中文版