⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 delphi_compile.txt

📁 很少见的DELPHI的编译指令说明文档
💻 TXT
📖 第 1 页 / 共 3 页
字号:
返回

DELPHI的编译指令

{$IFDEF WIN32} -- 这可不是批注喔! 
    对于Delphi来说﹐左右大括号之间的内容是批注﹐然而「{$」(左括号后紧接着货币符号)对于Compiler(编译器)而言并不是批注﹐而是写给Compiler看的特别指示。 

应用时机与场合 

    Delphi中有许许多多的Compiler Directives(编译器指令)﹐这些编译指令对于我们的程序发展有何影响呢? 它们又能帮我们什么忙呢? 

    Compiler Directive 对程序开发的影响与助益, 可以从以下几个方向来讨论: 

协助除错 
版本分类 
程序的重用与管理 
设定统一的执行环境 
协助除错 

    稳健熟练的程序设计师经常会在开发应用系统的过程中﹐特别加入一些除错程序或者回馈验算的程序﹐这些除错程序对于软件品质的提升有极其正面的功能。然而开发完成的正式版本中如果不需要这些额外的程序的话﹐要想在一堆程序中找出哪些是除错用的程序并加以删除或设定为批注﹐不仅累人﹐而且容易出错﹐况且日后维护时这些除错程序还用得着。

    此时如果能够应用像是$IFDEF的Compiler Directives ﹐就可以轻易的指示Delphi要/不要将某一段程序编进执行文件中。 
同时﹐Compiler本身也提供了一些错误检查的开关﹐可以预先对程序中可能的问题提醒程序设计师注意﹐同样有助于撰写正确的程序。 

版本分类 

    除了上述的除错版本/正式版本的分类之外﹐对于像是「试用版」「普及版」「专业版」的版本分类﹐也可以经由Compiler Directive的使用﹐为最后的产品设定不同的使用权限。其它诸如「中文版」「日文版」「国际标准版」等全球版本管理方面﹐同样也可以视需要指示Delphi特别连结哪些资源档或者是采用哪些适当的程序。以上的两则例子中﹐各版本间只需共享同一份程序代码即可。 

    Delphi 1.0 与 Delphi 2.0有许多不同之处﹐组件资源文件(.DCR)即是其中一例﹐两者的档案格式并不兼容﹐在您读过本文之后﹐相信可以写出这样的程序﹐指示Delphi在不同的版本采用适当的资源文件以利于组件的安装。 

{$IFDEF WIN32} 
{$R XXX32.DCR} 
{$ELSE} 
{$R XXXX16.DCR} 
{$EDNIF} 

程序的重用与管理 

    经过前文的讨论后﹐相信你已经不难看出Compiler Directives在程序管理上的应用价值。对于原始程序的重用与管理﹐也是Compiler Directives 使得上力的地方. 举例来说: 

    Pascal-Style字符串是Delphi 1.0与 Delphi 2.0之间的明显差异﹐除了原先的短字符串之外﹐Delphi 2.0之后还多了更为方便使用的长字符串﹐同时﹐系统也额外提供了像是 Trim() 这样的字符串处理函式。假如您有一个字符串处理单元必须要同时应用于Delphi 1.0 与 2.0的项目时﹐编译指示器可以帮你的忙。 

    此外﹐透过像是{$I xxxx} 这样的 Compiler Directives﹐我们也可以适当的含入某些程序, 同样有助于切割组合我们的程序或编译设定。 

设定一致的执行环境 

    项目小组的成员间﹐必须有共同的环境设定﹐我很难预料一个小组成员间彼此有不同的{$B}{$H}{$X}设定﹐最后子系统在并入主程序时会发生什么事。 

此外, 当您写好一个组件或单元需要交予第三者使用时, 使用编译指示器也可以保证组件使用者与您有相同的编译环境。 

使用Compiler Directives 

指令语法 

    Compiler Directives从外表看起来与批注颇为类似, 与批注不同的是:Compiler Directives的语法格式都是以「{$」开始, 不空格紧接一个名称(或一个字母)表明给Compiler的特别指示, 再加上其它的开关或参数内容, 最后以右大括号作为指令的结束, 例如: 
{$B+} 
{$R-} 
{$R MyCursor.res} 

    同时, 就如同Pascal的变量名称与保留字一样, Compiler Directives也是不区分大小写的。 
    从指令的语法格式来说Compiler Directives﹐可以进一步分类成以下三种格式:

开关指令(Switch directives) 
    这类指令都是单一字母以不空格的方式连接「+」或「-」符号; 或者是开关名称以一个空格后连接「ON」或「OFF」来表示作用/关闭某一个编译指示开关。例如: 

{$A+} 
{$ALIGN ON} 

    开关型的编译指令不一定要分行写, 它们可以组合在同一个编译指示的批注符号之间, 但必须以逗号连接, 而且中间不可以有空格, 例如: 
{$B+,H+,T-,J+} 

    光标停留在程序编辑器的任一位置时按下Ctrl+O O, 完整的Compiler Directives将会全部列于Unit的最上方。 

参数指令(Parameter directives) 
    有些Compiler Directives需要在编译名称后面连接自定的参数(文件名称或指定的内存大小), 例如: {$R MyCursor.res}, 即在指示Delphi在编译连结时, 含入「MyCursor.res」这个资源档。 

条件指令(Conditional directives) 
    指示Compiler在编译的过程中, 按我们设定的条件, 选择性的采用/排除不同区域的程序代码。 

    以下是一个条件编译的例子, 第一与第三列是写给Compiler看的,指示 Compiler在 __DEBUG这个条件名称完成定义的情况才编译ShowMessage()这列程序;反之, 如果 __DEBUG 当时没有定义的话, 这段程序几乎与批注无异, Compiler对它将视而不见。 
{$IFDEF __DEBUG} 
ShowMessage(IntToStr(i)); 
{$ENDIF} 

如何从IDE改变Compiler directives设定 

    从Delphi的IDE程序整合发展环境, 我们很方便的就可以修改各个compiler directives的设定, 方法是: 从Delphi IDE主选单: Project/Options/Compiler, 直接核选/取消各个CheckBox。值得注意的是, 改变一个项目的Compiler directives并不会影响其它的项目, 换言之, 各个项目都保有自己一套编译指示。 

    假如您希望其它的项目也采用相同一套的Compiler directives, 在上述ProjectOptions对话盒的左下方有一个「Default」选项, 选取这个CheckBox之后, 虽然对于既有的项目没有作用, 但未来新的项目都将可以采用这组设定作为默认值。
 
将Compiler directives写入程序 

    透过Delphi的整合环境设定Compiler directives的确十分简便, 但是许多情况下我们仍然需要将Compiler directive直接加到程序中。至少有两个原因支持我们这么作: 

局部控制编译条件 
    在Project/Options/Compiler中所作的设定, 影响所及是整个项目, 如果某一段程序要特别使用不同的编译设定, 就必须直接将编译指示加到程序中。 

    下列这段取自Online Help的程序范例, 即应用了{$I}编译指令局部控制在发生I/O错误时不要举发例外讯息, 这样, 我们就可以编译出一支在这段程序区域中不会产生I/O例外讯息的档案侦测函数。 

function FileExists(FileName: string): Boolean; 
var 
F: file; 
begin 
{$I-} 
AssignFile(F, FileName); 
FileMode := 0; ( Set file access to read only } 
Reset(F); 
CloseFile(F); 
{$I+} 
FileExists := (IOResult = 0) and (FileName <> ''''); 
end; { FileExists } 

程序的可移植性 

    我们都可能会用到其它公司或个人创作的unit或component, 也可能分享程序给其它人, 换句话说, 单元或程序可能会在不同的机器上编译, 直接将Compiler directives加入程序, 不仅可以免去程序使用前需要特别更改IDE的麻烦, 更重要的是解决了各个单元间要求不同编译环境的歧异。 

注意事项 

Compiler directives的作用与影响范围 

    如同变量的可见范围与生命周期, 在我们使用 Compiler Directives 时也必须注意各个Compiler Directives 的作用范围. 
Compiler Directives的作用范围可分为以下两种: 

全域的 
    全域的Compiler Directives, 影响所及是整个项目; 我们稍早前提到经由Delphi IDE改变Compiler directives的方式就属于全域的设定。 

区域的 
    而区域的Compiler Directives 影响所及只从Compiler Directives 改变的那一行开始, 直到该程序单元(Unit)的结束或另一个相同的Compiler Directives 为止, 对其它的程序单元并没有影响。 
也就是说, 如果在unit中特别加入Compiler directives, Compiler会优先采用区域的设定, 然后才是属于项目层级的全域设定。 

    值得一提的是, 在程序中直接加入Compiler directives的最大作用范围也只限于当时那个单元而已, 对其它单元并没有任何影响, 即使是以uses参考也是一样。

    也就是说, 我们可以透过uses参考其它unit公开的变量与函式, 但是各个unit的编译指令并不会互相参考。 

    这项独立的性质, 使得unit之间编译环境的设定与关系变得十分简洁, 例如Delphi 2.0的VCL都是在{$H+}的情况下编译的, 因此, VCL中的字符串都是以长字符串的型态编译而成的, 有了这项编译指令独立的特性, 不论我们Prject中的设定为何, 这些在VCL中定义过的字符串都是长字符串。我们的Project也不会因为uses了VCL中的unit而改变了自己的设定。 

    因此, 在我们移交程序到网络上时, 大可以放心的在程序中加入必要的Compiler directives, 别担心, 即使别的unit以uses参考了我们的程序, 也不影响它自己原来的设定。 

    如果我们自行以{$DEFINE _DEBUGVERSION}($DEFINE在稍后的个别指令介绍中将有说明)定义了一个条件符号, 这个新的条件符号也是区域的, 换句话说, 它只从定义的那一个单元的那一列之后才成立, 当然, 也只对目前这个单元有效. 

    由于自订的条件符号只有区域的作用, 如果有好几个程序单元都需要参考到某一个条件符号, 怎么办呢? 嗯! 在-一个程序单元开头处中都加上编译指示是最直接的方式, 可是略嫌麻烦, 特别是编译指示有变时, 要一一修正各个单元的设定内容, 很容易因为疏忽而出错。 

    比较简易可行的作法是从Delphi IDE整合发展环境的主选单-Project / Options/ Directories/Conditional的 Conditionals 中填入条件名称。这样, 相对于项目的各个unit而言, 就有了一个全域的条件符号。 

    或者, 您也可以参考本文对于{$I}这个Compiler Directive的说明。 我在那里指出了另一个弹性的解决方式。 

    修改过编译指令后, 建议Build All过一次程序.

    请试一试这个程序: 

procedure TForm1.Button1Click(Sender: TObject); 
begin 
// ifopt是用来侦测某一个编译开关的作用状态 
{$ifopt H+} 
ShowMessage(''H+''); 
{$else} 
ShowMessage(''H-''); 
{$endif} 
end; 

    在我们执行上述程序时, 在Delphi预设的是$H+时, ShowMessage()会在画面上会显示「H+」, 执行过后, 让程序与form的内容与位置保留不变, 单纯的从主选单: Project/Options/Compiler, 将Huge Strings的核对方块清除($H-), 然后按下F9执行,咦! 怎么还是看到「H+」?! 

    那是因为Delphi只会在unit内容经过异动后才会重新将.PAS编译成.DCU, 在我们的例子中, 程序并没有变动, .DCU当然也没有重新产生, 最后.EXE的这个部分自然也是没什么变化。 

    所以, 要解决这个问题, 只要以Delphi IDE主选单Project/Build All指示Delphi重新编译全部的程序即可。因此, 如果您从Delphi IDE修改过Compiler Directives后, 记得要Build All喔! 

不应该用来作为程序执行流程控制 
    在程序中, 我们可以使用if叙述, 根据执行当时的情况控制程序执行时的流程, 但我们不可以用{$IFDEF}来作同样的事, 为什么? 从上述的说明, 相信您不难发现, Compiler directives会对最后.EXE的内容发生直接的影响, 应用像是{$IFDEF}指示Compiler的结果, 几乎可以视同授权Compiler在编译的那个时候自动选用/舍弃程序到.DCU, .EXE中, 换句话说, 在程序编译完成时, 会执行到那一段程序已成定局了, 我们自然不能用它来作程序流程的控制。 

条件编译的巢套最多可以16层 
    在使用{$IFDEF}#{$ENDIF}条件编译我们的程序时, 一个{$IFDEF}中可以再包含另一个{$IFDEF}, 但深度最多只能16层, 虽然是个限制, 但以正常的情形来说, 这应该已经足够了。 

有些Compiler directives不应写在Unit中 
    对于像是{$MINSTACKSIZE}{$MAXSTACKSIZE}管理堆栈大小, 或者像是{$APPTYE}指示程序编译成图形/文字模式的Compiler directives, 只能写在.DPR中, 写在Unit中是没有效果的。 

建议事项 
确定您了解指令的影响 
    由于编译指令的影响是如此直接与深远, 在修改与应用某一个Compiler directive时, 请确定您已经了解其含意与影响。 

打开全部的侦错开关 
Delphi有关侦错的Compiler directives如下: 

$HINTS ON 
$D+ 
$L+ 
$Q+ 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -