Delphi 编译指令与说明

一个程序从无到有的过程是这样的编辑代码 -> 预处理 -> 编译(dcu) -> 链接(exe).
一、什么是预处理?
譬如 VCL 中有很多代码是兼容 Linux  Windows 下就需要在编译之前预处理掉那些 for Linux 的代码.
1、判断操作系统其中的 "MSWINDOWS"  "LINUX" 就是 Delphi 预定义的 "条件标识符".
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->


begin 
  {$IFDEF MSWINDOWS} 
    ShowMessage('Windows'); 
  {$ENDIF} 
 
  {$IFDEF LINUX} 
    ShowMessage('Linux'); 
  {$ENDIF} 
end;


2、自定义条件标识符(DEFINE): 下面例子中自定义了条件标识符: Nobird; 标识符和定义它的指令都不区分大小写但大家一般惯用大写.
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->


begin 
  {$DEFINE Nobird} 
  {$IFDEF Nobird} 
    ShowMessage('
标识符 Nobird 已定义'); 
  {$ELSE} 
    ShowMessage('标识符 Nobird 未定义'); 
  {$ENDIF} 
end; 


3、取消条件标识符的定义(UNDEF):
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->


begin 
  {$DEFINE Nobird} 
  {$IFDEF Nobird}  
    ShowMessage('
确认标识符 Nobird 是否定义'); 
  {$ENDIF} 
 
  {$UNDEF Nobird} 
  {$IFDEF Nobird}  
    ShowMessage('再次确认标识符 Nobird 是否定义'); 
  {$ENDIF} 
end; 


4、取消定义的简单办法 {$...}  $ 前面随便加点什么让它变成 "注释", 譬如: {.$}
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->


begin 
  {.$DEFINE Nobird} 
  {$IFDEF Nobird}  
    ShowMessage('
确认标识符 Nobird 是否定义'); 
  {$ENDIF} 
 
  {.$UNDEF Nobird} 
  {$IFDEF Nobird}  
    ShowMessage('再次确认标识符 Nobird 是否定义'); 
  {$ENDIF} 
end;


5、调试编译指令时特别要注意的: Delphi 有个常识如果单元代码没有改变相应的 dcu 不会重新生成!
因此为了有准确的调试结果执行前先用 Shift+F9 强制编译当前工程然后再 Run;
强制编译所有相关单元也可以方法: Project -> Build all project.
当然修改下代码也很方便譬如在代码中打个空格再退回来.
6、测试预定义的 Debug  Release: 当我们当新建一个工程, Delphi 默认的是调试(Debug)状态当我们发布软件时应该切换到发布(Release)状态.
两种状态下编译指令是有区别的 Release 状态下发布的 dcu  exe 会更小、更优化.
Debug  Release 的切换方法:
进入 Project Manager -> Build Configurations,  Debug  Release 上双击或从右键 Activate.
下面的代码可以检测到这种改变不过要注意上面提到的 Shift+F9  Project -> Build all project.
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->


begin 
  {$IFDEF DEBUG} 
    ShowMessage('
调试模式'); 
  {$ENDIF} 
 
  {$IFDEF RELEASE} 
    ShowMessage('发布模式'); 
  {$ENDIF} 
end;


7、编译指令写在哪?: 编译指令可以写在代码页的任何地方不过在代码的不同区域有时也会不同;
譬如: {$APPTYPE GUI}  {$APPTYPE CONSOLE} 就只能写在工程文件里才有效.
{$APPTYPE GUI}  {$APPTYPE CONSOLE} 分别表示窗口工程和控制台工程.
其中 {$APPTYPE GUI} 是默认的所以很少见到它.
它甚至可以嵌入到代码行当中譬如 ActnColorMaps 单元就有这么一句:
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->


begin 
  SystemParametersInfo(SPI_GETFLATMENU, 0, {$IFNDEF CLR}@{$ENDIF}FlatMenus, 0); 
end;


8、条件标识符的有效范围: Delphi 预定义的条件标识符都是全局的我们用 {$DEFINE ...} 自定义的标识符都是局部的.
如何自定义全局的标识符呢?
Project -> Options... -> 选定 Delphi Compiler -> 点击 Conditional defines 右边小按钮 -> 添加.
不过这和系统预定义的还是有区别咱们自定义的只能用于当前文件.
如何定义每个文件都可以使用的标识符呢?
 Project -> Options... 定义后马上选择左下角的 Default.
这和系统预定义的还是有区别因为这只能左右以后的文件管不着以前存在的文件.
如何...没办法了.
其他编译指令譬如在 Debug  Release 中的设置也都是这样也就是说每个文件都有相对独立的编译设置.
看到 Project -> Options... 马上明白了编译指令的设置方法有两种:
1、使用 {$...} 在代码中嵌入;
2、从 Project -> Options... 设置.
但在代码中嵌入有时是不可替代的譬如现在讨论的条件编译.
9、编译指令有多少?: 现在谈到的还只是条件编译实际应用最多的是开关编译在任一代码页执行快捷键 Ctrl+O+O , 然后看看最上面...
下面列出了这些默认设置:
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->


{$A8,B-,C+,D+,E-,F-,G+,H+,I+,J-,K-,L+,M-,N-,O+,P+,Q-,R-,S-,T-,U-,V+,W-,X+,Y+,Z1} 
{$MINSTACKSIZE $00004000} 
{$MAXSTACKSIZE $00100000} 
{$IMAGEBASE $00400000} 
{$APPTYPE GUI} 
{$WARN SYMBOL_DEPRECATED ON} 
{$WARN SYMBOL_LIBRARY ON} 
{$WARN SYMBOL_PLATFORM ON} 
{$WARN SYMBOL_EXPERIMENTAL ON} 
{$WARN UNIT_LIBRARY ON} 
{$WARN UNIT_PLATFORM ON} 
{$WARN UNIT_DEPRECATED ON} 
{$WARN UNIT_EXPERIMENTAL ON} 
{$WARN HRESULT_COMPAT ON} 
{$WARN HIDING_MEMBER ON} 
{$WARN HIDDEN_VIRTUAL ON} 
{$WARN GARBAGE ON} 
{$WARN BOUNDS_ERROR ON} 
{$WARN ZERO_NIL_COMPAT ON} 
{$WARN STRING_CONST_TRUNCED ON} 
{$WARN FOR_LOOP_VAR_VARPAR ON} 
{$WARN TYPED_CONST_VARPAR ON} 
{$WARN ASG_TO_TYPED_CONST ON} 
{$WARN CASE_LABEL_RANGE ON} 
{$WARN FOR_VARIABLE ON} 
{$WARN CONSTRUCTING_ABSTRACT ON} 
{$WARN COMPARISON_FALSE ON} 
{$WARN COMPARISON_TRUE ON} 
{$WARN COMPARING_SIGNED_UNSIGNED ON} 
{$WARN COMBINING_SIGNED_UNSIGNED ON} 
{$WARN UNSUPPORTED_CONSTRUCT ON} 
{$WARN FILE_OPEN ON} 
{$WARN FILE_OPEN_UNITSRC ON} 
{$WARN BAD_GLOBAL_SYMBOL ON} 
{$WARN DUPLICATE_CTOR_DTOR ON} 
{$WARN INVALID_DIRECTIVE ON} 
{$WARN PACKAGE_NO_LINK ON} 
{$WARN PACKAGED_THREADVAR ON} 
{$WARN IMPLICIT_IMPORT ON} 
{$WARN HPPEMIT_IGNORED ON} 
{$WARN NO_RETVAL ON} 
{$WARN USE_BEFORE_DEF ON} 
{$WARN FOR_LOOP_VAR_UNDEF ON} 
{$WARN UNIT_NAME_MISMATCH ON} 
{$WARN NO_CFG_FILE_FOUND ON} 
{$WARN IMPLICIT_VARIANTS ON} 
{$WARN UNICODE_TO_LOCALE ON} 
{$WARN LOCALE_TO_UNICODE ON} 
{$WARN IMAGEBASE_MULTIPLE ON} 
{$WARN SUSPICIOUS_TYPECAST ON} 
{$WARN PRIVATE_PROPACCESSOR ON} 
{$WARN UNSAFE_TYPE OFF} 
{$WARN UNSAFE_CODE OFF} 
{$WARN UNSAFE_CAST OFF} 
{$WARN OPTION_TRUNCATED ON} 
{$WARN WIDECHAR_REDUCED ON} 
{$WARN DUPLICATES_IGNORED ON} 
{$WARN UNIT_INIT_SEQ ON} 
{$WARN LOCAL_PINVOKE ON} 
{$WARN MESSAGE_DIRECTIVE ON} 
{$WARN TYPEINFO_IMPLICITLY_ADDED ON} 
{$WARN RLINK_WARNING ON} 
{$WARN IMPLICIT_STRING_CAST ON} 
{$WARN IMPLICIT_STRING_CAST_LOSS ON} 
{$WARN EXPLICIT_STRING_CAST OFF} 
{$WARN EXPLICIT_STRING_CAST_LOSS OFF} 
{$WARN CVT_WCHAR_TO_ACHAR OFF} 
{$WARN CVT_NARROWING_STRING_LOST OFF} 
{$WARN CVT_ACHAR_TO_WCHAR OFF} 
{$WARN CVT_WIDENING_STRING_LOST OFF} 
{$WARN XML_WHITESPACE_NOT_ALLOWED ON} 
{$WARN XML_UNKNOWN_ENTITY ON} 
{$WARN XML_INVALID_NAME_START ON} 
{$WARN XML_INVALID_NAME ON} 
{$WARN XML_EXPECTED_CHARACTER ON} 
{$WARN XML_CREF_NO_RESOLVE ON} 
{$WARN XML_NO_PARM ON} 
{$WARN XML_NO_MATCHING_PARM ON}


二、条件语句的更多用法
1. $IFDEF 等同于 $IF DEFINED(...) : 它们的结束分别是: $ENDIF$IFEND; 例子中的 VER200  Delphi 2009 的标识.
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->


begin 
  {$IFDEF VER200} 
    ShowMessage('
这是 Delphi 2009'); 
  {$ENDIF} 
 
  {$IF DEFINED(VER200)} 
    ShowMessage('这是 Delphi 2009'); 
  {$IFEND} 
end;


2. $IFNDEF 等同于 $IF NOT DEFINED(...) : 它们的结束分别是: $ENDIF$IFEND; 例子中的 VER150 Delphi 7 的标识.
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->


begin 
  {$IFNDEF VER150} 
    ShowMessage('
这不是 Delphi 7'); 
  {$ENDIF} 
 
  {$IF NOT DEFINED(VER150)} 
    ShowMessage('这不是 Delphi 7'); 
  {$IFEND} 
end; 


3. 可以使用 or  and:
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->


begin 
  {$DEFINE AAA} 
  {$DEFINE BBB} 
 
  {$IF DEFINED(AAA) OR DEFINED(BBB)} 
    ShowMessage('
条件标识符 AAA 和 BBB 其中一个定义了'); 
  {$IFEND} 
 
  {$IF DEFINED(AAA) AND DEFINED(BBB)} 
    ShowMessage('条件标识符 AAA 和 BBB 都定义了'); 
  {$IFEND} 
end;


4. 可以使用 System 单元里的常量我测试了 System 单元里的很多常量都没问题.
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->


begin 
  ShowMessage(FloatToStr(CompilerVersion)); {
在 Delphi 2009 中, CompilerVersion = 20.0} 
 
  {$IF CompilerVersion >= 17.0} 
    ShowMessage('这是 Delphi 2005 或以上的版本'); 
  {$IFEND} 
end; 


5. 使用 $IFOPT 判断编译开关: Delphi 挺好玩, 26个字母分别安排成不同的开关指令( Ctrl+o+o 查看,当然开关指令不止这些);
$IFOPT 可以判断这些指令是否打开.
这个指令不是很常用我看了一下 2009  VCL 源码总共才用了 6 .
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->


begin 
  {$IFOPT B+} 
    ShowMessage('
指令 B 已打开'); 
  {$ELSE} 
    ShowMessage('指令 B 已关闭'); 
  {$ENDIF} 
 
  {$B+} 
  {$IFOPT B+} 
    ShowMessage('Ok!'); 
  {$ENDIF} 
end;




















<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->

指令及默认值

可选值

范围

注释

举例

{$A8}
{$ALIGN8}

{$A+},{$A-},
{$A1},{$A2},{$A4},{$A8};
{$ALIGN ON},{$ALIGN OFF},
{$ALIGN 1},{$ALIGN 2},
{$ALIGN 4},{$ALIGN 8} 

Local

  

{$APPTYPE GUI}

{$APPTYPE GUI},
{$APPTYPE CONSOLE}

Global

  

{$B-}
{$BOOLEVAL OFF} 

{$B+},{$B-};
{$BOOLEVAL ON},
{$BOOLEVAL OFF} 

Local

  

{$C+}
{$ASSERTIONS ON} 

{$C+},{$C-};
{$ASSERTIONS ON},
{$ASSERTIONS OFF} 

Local

  

{$D+}
{$DEBUGINFO ON}

{$D+},{$D-}
{$DEBUGINFO ON},
{$DEBUGINFO OFF}

Global

  

{$DENYPACKAGEUNIT OFF}

{$DENYPACKAGEUNIT ON},

Local

  

{$DESCRIPTION 'text'}

 

Global

  

{$DESIGNONLY OFF}

{$DESIGNONLY ON},
{$DESIGNONLY OFF}

Local

  

{$E-}

{$E+},{$E-}

 

  

{$E extension}
{$EXTENSION extension} 

 

   

{$EXTERNALSYM identifier}

    

{$F-}

{$F+},{$F-}

 

  

{$FINITEFLOAT ON}

{$FINITEFLOAT ON},
{$FINITEFLOAT OFF}

Global

  

{$G+}
{$IMPORTEDDATA ON}

{$G+},{$G-};
{$IMPORTEDDATA ON},
{$IMPORTEDDATA OFF} 

Local

  

{$H+}
{$LONGSTRINGS ON}

{$H+},{$H-}
{$LONGSTRINGS ON},
{$LONGSTRINGS OFF} 

Local

  

{$HINTS ON}

{$HINTS ON},
{$HINTS OFF}

Local

  

{$HPPEMIT 'string'}

    

{$I filename}
{$INCLUDE filename} 

 

Local

  

{$I+}
{$IOCHECKS ON}

{$I+},{$I-};
{$IOCHECKS ON},
{$IOCHECKS OFF} 

Local

 

 

{$IMAGEBASE $00400000}

{$IMAGEBASE number}

Global

  

{$IMPLICITBUILD ON},{$IMPLICITBUILD OFF}

{$IMPLICITBUILD ON}

Global

  

{$J-}
{$WRITEABLECONST OFF} 

{$J+},{$J-}
{$WRITEABLECONST ON},
{$WRITEABLECONST OFF} 

Local

  

{$K-}

{$K+},{$K-}

   

{$L+}
{$LOCALSYMBOLS ON}

{$L+},{$L-}
{$LOCALSYMBOLS ON},
{$LOCALSYMBOLS OFF} 

Global

  

{$L filename}
{$LINK filename} 

 

Local

 

 

$LIBPREFIX 'lib' or $SOPREFIX 'bpl' 
$LIBSUFFIX ' '
$LIBVERSION ' ' 

$LIBPREFIX 'string'
$LIBSUFFIX 'string'
$LIBVERSION 'string' 

Global

 

 

{$M-}
{$TYPEINFO OFF} 

{$M+},{$M-}
{$TYPEINFO ON},
{$TYPEINFO OFF} 

Local

  

{$M 16384,1048576} 

{$M minstacksize,maxstacksize};
{$MINSTACKSIZE number}
{$MAXSTACKSIZE number} 

 

 

 

{$M 1048576} 

{$M reservedbytes}
{$RESOURCERESERVE reservedbytes} 

Global

Linux

 

{$MESSAGE HINT|WARN|ERROR|FATAL 'text string'} 

 

Local

 

 

{$METHODINFO OFF} 

{$METHODINFO ON},
{$METHODINFO OFF} 

 

 

 

{$N+}

{$N+},{$N-}

   

{$NODEFINE identifier} 

 

 

 

 

{$NOINCLUDE filename} 

 

 

 

 

{$O+}
{$OPTIMIZATION ON} 

{$O+},{$O-};
{$OPTIMIZATION ON},
{$OPTIMIZATION OFF} 

Local

  

{$ObjExportAll Off}

{$ObjExportAll On},
{$ObjExportAll Off}

Global

  

{$P+}
{$OPENSTRINGS ON}

{$P+},{$P-}
{$OPENSTRINGS ON},
{$OPENSTRINGS OFF} 

Local

  

{$POINTERMATH OFF} 

{$POINTERMATH ON},
{$POINTERMATH OFF} 

Local

 

 

{$Q-}
{$OVERFLOWCHECKS OFF} 

{$Q+},{$Q-}
{$OVERFLOWCHECKS ON},
{$OVERFLOWCHECKS OFF} 

Local

  

{$R filename}
{$RESOURCE filename}
{$R *.xxx}
{$R filename.res filename.rc} 

 

 

 

 

{$R-}
{$RANGECHECKS OFF} 

{$R+},{$R-}
{$RANGECHECKS ON},
{$RANGECHECKS OFF} 

Local

  

{$REALCOMPATIBILITY OFF} 

{$REALCOMPATIBILITY ON},
{$REALCOMPATIBILITY OFF} 

Local

 

 

{$RUNONLY OFF} 

{$RUNONLY ON},
{$RUNONLY OFF} 

Local

 

 

{$S-}

{$S+},{$S-}

   

{$SetPEFlags <integer expression>}
{$SetPEOptFlags <integer expression>} 

 

Local

 

 

{$T-}
{$TYPEDADDRESS OFF} 

{$T+},{$T-}
{$TYPEDADDRESS ON},
{$TYPEDADDRESS OFF} 

Global

  

{$U-}
{$SAFEDIVIDE OFF} 

{$U+},{$U-}
{$SAFEDIVIDE ON},
{$SAFEDIVIDE OFF} 

Local

  

{$V+}
{$VARSTRINGCHECKS ON}

{$V+},{$V-}
{$VARSTRINGCHECKS ON},
{$VARSTRINGCHECKS OFF} 

Local

  

{$W-}
{$STACKFRAMES OFF} 

{$W+},{$W-}
{$STACKFRAMES ON},
{$STACKFRAMES OFF} 

Local

  

{$WARN ON} 

{$WARN identifier ON},
{$WARN identifier OFF} 

Local

 

 

{$WARNINGS ON} 

{$WARNINGS ON},
{$WARNINGS OFF} 

Local

 

 

{$WEAKPACKAGEUNIT OFF} 

{$WEAKPACKAGEUNIT ON},
{$WEAKPACKAGEUNIT OFF} 

Local

 

 

{$X+}
{$EXTENDEDSYNTAX ON} 

{$X+},{$X-};
{$EXTENDEDSYNTAX ON},
{$EXTENDEDSYNTAX OFF} 

Global

  

{$YD}
{$DEFINITIONINFO ON}  

{$Y+},{$Y-},{$YD};
{$REFERENCEINFO ON},
{$REFERENCEINFO OFF};
{DEFINITIONINFO ON},
{DEFINITIONINFO OFF} 

Global

  

{$Z1}
{$MINENUMSIZE 1} 

{$Z1},{$Z2},{$Z4};
{$MINENUMSIZE 1},
{$MINENUMSIZE 2},
{$MINENUMSIZE 4} 

Local

  
     

$DEFINE
$UNDEF
$IFDEF
$ELSE
$ENDIF
{$IF DEFINED(...)}
{$IFEND}
{$IF NOT DEFINED(...)}
{$IFEND}
{$IF DEFINED(...) OR DEFINED(...)}
{$IFEND}
{$IF DEFINED(...) AND DEFINED(...)}
{$IFEND}
{$IF System.Const >= Number}
{$IFEND}
{$IFOPT ...}
{$ELSE}
{$ENDIF}

 

 

 

 

 

 

 

 

 

{$region 'text'} ... {$endregion}

    

 

 

 

 

{$IFDEF WIN32}

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


        应用时机与场合
        Delphi中有许许多多的Compiler Directives(编译器指令)﹐这些编译指令对于我们的程序发展有何影响呢?它们又能帮我们什么忙呢?
        Compiler Directive 对程序开发的影响与助益可以从以下几个方向来讨论:
        <!--[if gte vml 1]><v:shapetype id="_x0000_t75" coordsize="21600,21600" o:spt="75" o:preferrelative="t" path="m@4@5l@4@11@9@11@9@5xe" filled="f" stroked="f"> <v:stroke joinstyle="miter"/> <v:formulas> <v:f eqn="if lineDrawn pixelLineWidth 0"/> <v:f eqn="sum @0 1 0"/> <v:f eqn="sum 0 0 @1"/> <v:f eqn="prod @2 1 2"/> <v:f eqn="prod @3 21600 pixelWidth"/> <v:f eqn="prod @3 21600 pixelHeight"/> <v:f eqn="sum @0 0 1"/> <v:f eqn="prod @6 1 2"/> <v:f eqn="prod @7 21600 pixelWidth"/> <v:f eqn="sum @8 21600 0"/> <v:f eqn="prod @7 21600 pixelHeight"/> <v:f eqn="sum @10 21600 0"/> </v:formulas> <v:path o:extrusionok="f" gradientshapeok="t" o:connecttype="rect"/> <o:lock v:ext="edit" aspectratio="t"/> </v:shapetype><v:shape id="图片_x0020_1" o:spid="_x0000_i1030" type="#_x0000_t75" alt="http://st.blog.163.com/style/common/htmlEditor/portrait/face/preview/face50.gif" style='width:14.25pt;height:14.25pt;visibility:visible;mso-wrap-style:square'> <v:imagedata src="file:///C:\Users\liangr\AppData\Local\Temp\msohtmlclip1\01\clip_image001.gif" o:title="face50"/> </v:shape><![endif]--><!--[if !vml]-->http://st.blog.163.com/style/common/htmlEditor/portrait/face/preview/face50.gif<!--[endif]-->  协助除错
        <!--[if gte vml 1]><v:shape id="图片_x0020_2" o:spid="_x0000_i1029" type="#_x0000_t75" alt="http://st.blog.163.com/style/common/htmlEditor/portrait/face/preview/face50.gif" style='width:14.25pt;height:14.25pt;visibility:visible;mso-wrap-style:square'> <v:imagedata src="file:///C:\Users\liangr\AppData\Local\Temp\msohtmlclip1\01\clip_image001.gif" o:title="face50"/> </v:shape><![endif]--><!--[if !vml]-->http://st.blog.163.com/style/common/htmlEditor/portrait/face/preview/face50.gif<!--[endif]-->  版本分类
        <!--[if gte vml 1]><v:shape id="图片_x0020_3" o:spid="_x0000_i1028" type="#_x0000_t75" alt="http://st.blog.163.com/style/common/htmlEditor/portrait/face/preview/face50.gif" style='width:14.25pt;height:14.25pt;visibility:visible;mso-wrap-style:square'> <v:imagedata src="file:///C:\Users\liangr\AppData\Local\Temp\msohtmlclip1\01\clip_image001.gif" o:title="face50"/> </v:shape><![endif]--><!--[if !vml]-->http://st.blog.163.com/style/common/htmlEditor/portrait/face/preview/face50.gif<!--[endif]-->  程序的重用与管理
        <!--[if gte vml 1]><v:shape id="图片_x0020_4" o:spid="_x0000_i1027" type="#_x0000_t75" alt="http://st.blog.163.com/style/common/htmlEditor/portrait/face/preview/face50.gif" style='width:14.25pt;height:14.25pt;visibility:visible;mso-wrap-style:square'> <v:imagedata src="file:///C:\Users\liangr\AppData\Local\Temp\msohtmlclip1\01\clip_image001.gif" o:title="face50"/> </v:shape><![endif]--><!--[if !vml]-->http://st.blog.163.com/style/common/htmlEditor/portrait/face/preview/face50.gif<!--[endif]-->  设定统一的执行环境


        协助除错
        稳健熟练的程序设计师经常会在开发应用系统的过程中﹐特别加入一些除错程序或者回馈验算的程序﹐这些除错程序对于软件品质的提升有极其正面的功能。然而开发完成的正式版本中如果不需要这些额外的程序的话﹐要想在一堆程序中找出哪些是除错用的程序并加以删除或设定为批注﹐不仅累人﹐而且容易出错﹐况且日后维护时这些除错程序还用得着。
        此时如果能够应用像是{$IFDEF}Compiler Directives ﹐就可以轻易的指示Delphi/不要将某一段程序编进执行文件中。同时﹐Compiler本身也提供了一些错误检查的开关﹐可以预先对程序中可能的问题提醒程序设计师注意﹐同样有助于撰写正确的程序。
        版本分类
        除了上述的除错版本/正式版本的分类之外﹐对于像是「试用版」「普及版」「专业版」的版本分类﹐也可以经由Compiler Directive的使用﹐为最后的产品设定不同的使用权限。其它诸如「中文版」「日文版」「国际标准版」等全球版本管理方面﹐同样也可以视需要指示Delphi特别连结哪些资源档或者是采用哪些适当的程序。以上的两则例子中﹐各版本间只需共享同一份程序代码即可。
        组件资源文件(.DCR)即是其中一例﹐两者的档案格式并不兼容﹐在您读过本文之后﹐相信可以写出这样的程序﹐指示Delphi在不同的版本采用适当的资源文件以利于组件的安装。
        {$IFDEF WIN32} 
        {$R XXX32.DCR} 
        {$ELSE} 
        {$R XXXX16.DCR} 
        {$EDNIF}
        
程序的重用与管理
        经过前文的讨论后﹐相信你已经不难看出Compiler Directives在程序管理上的应用价值。对于原始程序的重用与管理﹐也是Compiler Directives 使得上力的地方举例来说
        Pascal-Style字符串之间的明显差异﹐除了原先的短字符串之外﹐之后还多了更为方便使用的长字符串﹐同时﹐系统也额外提供了像是 Trim() 这样的字符串处理函式。假如您有一个字符串处理单元必须要同时应用于Delphi不同的项目时﹐编译指示器可以帮你的忙。
        此外﹐透过像是{$I xxxx} 这样的 Compiler Directives﹐我们也可以适当的含入某些程序同样有助于切割组合我们的程序或编译设定。
        设定一致的执行环境
        项目小组的成员间﹐必须有共同的环境设定﹐我很难预料一个小组成员间彼此有不同的{$B}{$H}{$X}设定﹐最后子系统在并入主程序时会发生什么事。此外当您写好一个组件或单元需要交予第三者使用时使用编译指示器也可以保证元件使用者与您有相同的编译环境。

        使用Compiler Directives

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

        同时就如同Pascal的变量名称与保留字一样Compiler Directives也是不区分大小写的。从指令的语法格式来说Compiler Directives﹐可以进一步分类成以下三种格式:
        1,  开关指令(Switch directives)
        这类指令都是单一字母以不空格的方式连接「+」或「-」符号或者是开关名称以一个空格后连接「ON」或「OFF」来表示作用/关闭某一个编译指示开关。例如:
        {$A+}
        {$ALIGN ON}

        开关型的编译指令不一定要分行写它们可以组合在同一个编译指示的批注符号之间,但必须以逗号连接而且中间不可以有空格例如:
        {$B+,H+,T-,J+}
         光标停留在程序编辑器的任一位置时按下Ctrl+O O, 完整的Compiler Directives将会全部列于Unit的最上方。
          2, 参数指令(Parameter directives) 
         有些Compiler Directives需要在编译名称后面连接自定的参数(文件名称或指定的记忆体大小), 例如{$R MyCursor.res}即在指示Delphi在编译连结时含入「MyCursor.res」这个资源档。
          3,  条件指令(Conditional directives) 
        指示Compiler在编译的过程中按我们设定的条件选择性的采用/排除不同区域的程序代码。以下是一个条件编译的例子第一与第三列是写给Compiler看的,指示 Compiler __DEBUG这个条件名称完成定义的情况才编译ShowMessage()这列程序;反之如果 __DEBUG 当时没有定义的话这段程序几乎与批注无异, Compiler对它将视而不见。 
        {$IFDEF __DEBUG}
        ShowMessage(IntToStr(i));
        {$ENDIF}

        <!--[if gte vml 1]><v:shape id="图片_x0020_5" o:spid="_x0000_i1026" type="#_x0000_t75" alt="http://st.blog.163.com/style/common/htmlEditor/portrait/face/preview/face50.gif" style='width:14.25pt;height:14.25pt;visibility:visible;mso-wrap-style:square'> <v:imagedata src="file:///C:\Users\liangr\AppData\Local\Temp\msohtmlclip1\01\clip_image001.gif" o:title="face50"/> </v:shape><![endif]--><!--[if !vml]-->http://st.blog.163.com/style/common/htmlEditor/portrait/face/preview/face50.gif<!--[endif]--> 如何从IDE改变Compiler directives设定
        DelphiIDE程序整合发展环境我们很方便的就可以修改各个compiler directives的设定方法是:Delphi IDE主选单: Project/Options/Compiler, 直接核选/取消各个CheckBox。值得注意的是改变一个项目的Compiler directives并不会影响其它的项目换言之,各个项目都保有自己一套编译指示。
        假如您希望其它的项目也采用相同一套的Compiler directives, 在上述Project Options对话盒的左下方有一个「Default」选项选取这个CheckBox之后虽然对于既有的项目没有作用但未来新的项目都将可以采用这组设定作为默认值。
        <!--[if gte vml 1]><v:shape id="图片_x0020_6" o:spid="_x0000_i1025" type="#_x0000_t75" alt="http://st.blog.163.com/style/common/htmlEditor/portrait/face/preview/face50.gif" style='width:14.25pt;height:14.25pt;visibility:visible;mso-wrap-style:square'> <v:imagedata src="file:///C:\Users\liangr\AppData\Local\Temp\msohtmlclip1\01\clip_image001.gif" o:title="face50"/> </v:shape><![endif]--><!--[if !vml]-->http://st.blog.163.com/style/common/htmlEditor/portrait/face/preview/face50.gif<!--[endif]-->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 }
         程序的可移植性
        我们都可能会用到其它公司或个人创作的unitcomponent, 也可能分享程序给其它人换句话说单元或程序可能会在不同的机器上编译直接将Compiler directives加入程序不仅可以免去程序使用前需要特别更改IDE的麻烦更重要的是解决了各个单元间要求不同编译环境的歧异。
        注意事项
        Compiler directives的作用与影响范围如同变量的可见范围与生命周期在我们使用 Compiler Directives 时也必须注意各个Compiler Directives 的作用范围.
        Compiler Directives的作用范围可分为以下两种:
       1,全域的
       全域的Compiler Directives, 影响所及是整个项目我们稍早前提到经由Delphi IDE改变Compiler directives的方式就属于全域的设定。
       2,区域的
        而区域的Compiler Directives 影响所及只从Compiler Directives 改变的那一行开始直到该程序单元(Unit)的结束或另一个相同的Compiler Directives 为止对其他的程序单元并没有影响。也就是说如果在unit中特别加入Compiler directives, Compiler会优先采用区域的设定然后才是属于项目层级的全域设定。
        值得一提的是在程序中直接加入Compiler directives的最大作用范围也只限于当时那个单元而已对其他单元并没有任何影响即使是以uses参考也是一样。也就是说我们可以透过uses参考其它unit公开的变量与函式但是各个unit的编译指令并不会互相参考。
        这项独立的性质使得unit之间编译环境的设定与关系变得十分简洁例如Delphi 2.0VCL都是在{$H+}的情况下编译的因此, VCL中的字符串都是以长字符串的型态编译而成的有了这项编译指令独立的特性不论我们Prject中的设定为何这些在VCL中定义过的字符串都是长字符串。我们的Project也不会因为usesVCL中的unit而改变了自己的设定。
        因此在我们移交程序到网络上时大可以放心的在程序中加入必要的Compiler directives, 别担心即使别的unituses参考了我们的程序也不影响它自己原来的设定。
        如果我们自行以{$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中是没有效果的。
        建议事项
        1,确定您了解指令的影响
        由于编译指令的影响是如此直接与深远在修改与应用某一个Compiler directive请确定您已经了解其含意与影响。
        2,打开全部的侦错开关
        Delphi有关侦错的Compiler directives如下:
          $HINTS ON
          $D+
         $L+
         $Q+
         $R+
        $WARNINGS ON

        各指令的用法您可以参阅本章稍后对个别指令的说明全部打开这些开关吧这样不仅让您可以使用Delphi IDE的除错器对于程序编译与执行过程中的问题, Delphi也会适时的反应有助于写作正确的程序。
        此处有一个迷惑有待澄清-「加入Dubug信息会不会让执行文件变大变慢啊?不一定。对于们像是$D+, $L+, $HINTS ON这些开关打开后, Delphi在编译时的确会额外加入一些除错信息使得.DCU的档案变大对于.EXE的档案大小并没有影响同时程序的执行速度也没有改变还可以应用IDE的除错器trace我们的程序值得应用。
        对于像是$Q, $RCompiler directive, 的确会影响执行文件的大小与速度然而这并不动摇我们在研发期间使用它们的决定请想想看值得为这一点点的速度放弃程序的正确性吗当然程序开发完成后正式出货的版本可以关闭这两个开关。
 
        如果您写好了一个组件而且只预备提供.DCU, 由于没有.PAS可供Delphi IDEDebugger追踪程序除错开关反而应该在组件脱手前关闭并重新编译.DCU, 否则会引起使用者那边找不到档案的例外讯息。
       善用{$I}
        {$I FileName}是一个非常有用的Compiler directive.应用这个指令我们可以弹性的管理Compiler directive的设定。
       条件名称请加入前导符
        不知道您有没有这个疑问 -- 如果用{$DEFINE}定义的条件名称与变量名称相同时会发生什么事
procedure TForm1.Button1Click(Sender: TObject);
var
  TEST: integer;
begin
{$DEFINE TEST}
{$IFDEF TEST}
   ShowMessage('Test');
{$ENDIF}
end;
        以上的程序编译与执行都没有问题但条件名称与变量名称重复毕意容易让人混淆因此,假如能适当的为编译条件名称之前加上诸如底线(_TEST), 程序会比较容易阅读。
        设定一致的编译环境
        在您了解了Compiler Directives之后请立即开始着手修改您IDE中有关编译指示的各个开关并且设为Default,这样日后您的项目乃至整个研发小组都将拥有共同一致的编译环境对于写出来的程序会以何种方式编译连结都了然于胸直接有助于子系统顺利并入主系统中。


        个别指令说明
        有了之前对于Compiler directives的观念之后接下来的这一节我将一一介绍几个常用的Compiler Directive的用法与注意事项您可以从这一节中学到更多有关Compiler directives的知识与使用细节。
        {$A+} 字段对齐
        {$A+}(默认值)的情形下如果没有使用 packed 修饰词宣告的 record 型态其字段会以CPU可以有效存取的方式向 1. 2. 4 等边界对齐以获取最佳的存取速度。以下列的程序示例来说:
{$A+}
type
  MyRecord = record
    ByteField: byte;
    IntegerField: integer;
  end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  ShowMessage(IntToStr(SizeOf(MyRecord)));
end;
        ShowMessage{$A+}时显示的结果是:8倘若是{$A-}, 那所得的结果是「5,

        按理说, Byte应该只要一个byte就足够了但是考虑到硬件的执行特性经过对齐后的record会有比较好的执行速度。
        有关这个Compiler Directive要注意的事项是不管{$A}的开关是ONOFF, 使用packed修饰过的记录宣告是一定不会对齐的例如
          MyRecord = packed record // 不会对齐的记录宣告方式
        {$APPTYPE GDI} 应用程序型态
        一般的情形下, Delphi会以{$APPTYPE GUI}的方式产生一个图形的使用者接口程序如果您需要产生一个文字屏幕模式的程序那可以经由:
         .DPR中加入{$APPTYPE CONSOLE}
         从主选单: Project/Options/Linker/EXE and DLL Options, 核取Generate Console ApplicationCheck Box
        其它有关这个Compiler Directive的注意事项有:
         $APPTYPE不能应用在DLL的项目或单一的程序单元(Unit), 它只对.EXE有意义。而且只有写在.DPR中才有作用。
         我们可以应用System程序单元中的IsConsole函数在程序执行时侦测应用程序的类型。
         参阅Object Pascal手册第十三章可以知道更多有关Console Mode Application的信息。
        {$B-} 布尔评估
        请看以下的程序:
  if (Length(sCheckedDateString) <> 8)
    or EmptyStr(sCheckedDateString)
    or (sCheckedDateString = '  .  .  ')
    or (sCheckedDateString = '  /  /  ') then
  begin
    Result := True;
    Exit;
  end;
        假如sCheckedDateString的字符串内容是「85/12/241(长度9)的话以上的if述句其实在第一个逻辑判断时就已经知道结果了即使不看后来的逻辑运算结果也知道整个式子会是真值。
        假如您希望对整个逻辑表达式进行完整的评估 -- 尽管结果已知后来的逻辑运算也不影响整个的结果时仍要全部评估过请将这个Compiler directives设为{$B+}, 反之请设为{$B-}, 系统的默认值是{$B-}
        {$D+} 除错信息
        当程序以{$D+}(默认值)编译时我们可以用Delphi整合发展境境的Debugger设定断点也可以使用Trace IntoTrace Info追踪程序的执行过程值得注意的是{$D+}编译的程序执行的速度并不会受到影响只不过编译过的DCU的档案长度会加大EXE档的大小不变。
        {$DEFINE条件名称定义条件名称
        随着您对Compiler Directives的了解与应用程度的加深您会发现这是一个非常实用的编译指示。经常我们会因为除错需要﹑区别不同版本等缘故希望选择性的采用或排除某一段程序这个时候我们就可以先以$DEFINE定义好一个条件名称(Conditional name), 然后配合{$IFDEF条件名称}…{$ELSE}…{$ENDIF}指示编译器按指定的条件名称之有无来选择需要编译的程序。以下列的程序片断来说:
{$DEFINE _ProVersion}

procedure TForm1.Button1Click(Sender: TObject);
begin
{$IFDEF _Proversion}
   frmPrint.ShowModal;  // A
{$ELSE}
   ShowMessage('很抱歉试用版不提供打印功能');
{$ENDIF}
end;
        编译器将会选择编译上述A的那列程序日后如果我们需要编译「简易版」的程序版本时只要{$DEFINE _ProVersion}那列整个删掉。或者{$DEFINE _ProVersion}改成{-$DEFINE _ProVersion}, 让它变成普通的批注或者{$DEFINE _ProVersion}的下一列加上{$UNDEF _ProVersion}, 解除_ProVersion这个条件名称的定义。
这样由于_ProVersion这个条件名称未定义的缘故, Compiler就只会选择{$ELSE}下的那段程序重新编译一次不需费太多力气很容易的就可以制作出「简易版」了省去了要同时维护两份程序的麻烦。
        使用$DEFINE时的其它注意事项如下:
        {$DEFINE}定义的条件名称都是区域的。换句话说它的作用范围只在当时所在的单元才有效即使定义在unitinterface, 由其它的unituses参考也没有效仍然只有在目前的unit有作用。
        此外它的作用范围是从定义起unit结尾或者以{$UNDEF}解除为止。
        如果程序单元中已经用{$DEFINE}定义了一个条件名称而且也没有用{$UNDEF}解除定义重新{$DEFINE}一个同样名称并没有作用换句话说它们是同一个.
        假如需要一个全域的条件名称您可以:主选单: Project / Options / Directories/Conditional  Conditionals 中填入条件名称。

         以下的标准条件名称Delphi 已经预先预备好的我们可以直接引用同时它们都是全域的任何Unit都可以参照得到。
         VER90: Delphi Object Pascal的版本编号。90表示9.0日后若出现9.5版时也会有VER95的定义。
         WIN32: 指出目前是在Win32(95, NT)作业环境
         CUP386: 采用386()以上的CPU系统会提供本条件名称。
         CONSOLE: 此符号会于应用程序是在屏幕模式下编译时才定义。
        {$DESCRIPTION  描述内容}
        应用{$DESCRIPTION}可以指定加入一段文字到.EXE.DLL表头的模块描述进入点(module description entry)中﹐通常我们会用这个Compiler Directive加入应用程序的名称与版本编号到.EXE中。例如:
        {$DESCRIPTION Dchat Version 1.0}
        {$X+}  扩充语法

        这是为了与之前的Pascal版本前向兼容的编译指令虽然设定这个开关型的指令仍有作用但笔者建议您大可保留系统的默认值{$X+}, {$X+}:
         不需要非得准备一个变量接受函数的传回值换句话说函数的传回值可以舍弃此时就可以像是呼叫程序一样很方便的呼叫函数。
         支持Pchar型态与零基的字符数组作为C语言以Null结尾的字符串。
        {$HINTS OFF}  提示讯息
         打关{$HINTS}开关后, Compiler会提示程序设计师注意以下的情况:
           变量定义了却没有使用
           程序流程中不会执行的forwhile循环
         只有存入没有取用的指定叙述。意思是说指定数据到某一个变量之后却没有任何的程序参考取用这个变量值。
{$HINTS ON}
procedure MyTest;
const _False = False;
var
  I, J: integer;
begin
  if
 _False then
    for
 I := 1 to 3 do ;
  J := 3;
end;
{$HINTS OFF}
      由于程序简单在两个$HINTS中间的程序我们不难看出:
        for循环不会执行到, I 变量也因此不曾用过
        J := 3写了等于白写
        但在程序越写越长而日趋复杂时藉由{$HINTS ON}的协助比较容易察觉出程序的毛病。
        {$IFDEF} {$IFNDEF}
        请参阅{$DEFINE}的说明在此补充说明{$IFNDEF}, 以下列程序来说即在指示Compiler_Test未定义时条件编译ShowMessage()那列程序:
{$IFNDEF _TEST}
  ShowMessage('_TEST not define');
{$ENDIF}
        换言之, {$IFNDEF}相当于{$IFDEF}{$ELSE}部分。
        {$IFOPT 开关}
        到底{$B}是开着或关着呢如果我们想要指示Compiler按照某一个编译开关当时的状态作我们指定的事应该该怎么做呢这时, {$IFOPT}就派得上用场了。例如:
{$R+}
{$Q-}
  // 特别指定为Q-
{$IFOPT R+} // 如果 Range Check 是开启的话
  ShowMessage('程序是在 Range Check 开启状态下编译的');
  // 这个 Q+ 也会在 IFOPT R+ 成立时才通知 Compiler
  {$Q+}
{$ENDIF}
{$IFOPT Q+}

  ShowMessage('Q 也变成开启状态了');
{$ENDIF}
        ShowMessage()  {$Q+}会在$R+ 的情形下才编译因此虽然我们事前特别指示为{$Q-}, 第二个的ShowMessage()在程序执行时也可以看到「也变成开启状态了」。
        {$IMAGEBASE档案基础地址}
        这个Compiler directive用来指示.EXE.DLL加载时的预设地址。例如: {$IMAGEBASE $00400000}。如果指定加载的地址空间之前已经有其它模块占用了, Windows会为.EXE重新配置一个新的加载地址。对于.DLL来说如果可以成功配置到我们写在{$IMAGEBASE}的地址由于不需要重新配置内存地址不仅加载的速度较快如果有其它程序也参照到这个DLL的话也可以减少加载时间与内存的消耗。
        使用这个Compiler directive时需要注意的事项有:
         指定的叙述必须是一个大于$0001000032位整数数值同时较低位置的16个位必须是零。
         DLL的建议地址范围从$40000000$7FFFFFFF, 该范围的地址可以同时适用于Windows 95Windows NT
        {$I文件名称含入档案
        Delphi IDE修改Compiler directives的确相当方便但往往我们仍然需要将Compiler directives直接加入程序中可是当我们这样作之后不用多久就会发现要一一重新修改各个单元中的这些Compiler directives实在是既无聊而又容易出错的工作。这时候假如您一开始就采用{$I文件名称}, 整件事就会变得很简单。怎么做呢
        让我用一个例子告诉您 
        先用一般的文书编辑器建好一个MySet.inc的普通文本文件内容为:
{$H+}
{$DEFINE _Proversion}
        在我们的程序中加入一列{$I MySet.inc}, 例如:
unit Unit1;
{$I MySet.inc}

interface

implementation

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
begin
{$IFDEF _ProVersion}
  ShowMessage('专业版');
{$Else}
  ShowMessage('只有专业版才有此功能');
{$ENDIF}
end;

        这是子程序的观念嘛没错就是这么简单而已以后如果有任何变化修改MySet.INC, 然后Project/Buile All即可实在是够简单的了。
        基本动作会了之后让我告诉你多一点有关{$I文件名称}的事。
         一旦应用了{$I文件名称}, 几乎等于Compiler在编译时Compiler将这个档案的内容贴进我们的程序中的那个位置。
         如果没有注明扩展名, Delphi预设这个档案是.PAS
         如果在项目的目录中找不到这个档案的话, Delphi会陆续搜寻Tools/Options/Library中的Library Path中的目录。
        另外当您写作了一个DLL, 使用者在使用其中的函数前必须宣告过如果能够一并提供这些函数的宣告文件,使用者只要一行{$I xxx}即可是不是很方便呢?
        {$I+}  EInOutError检查
        {$I+}(系统默认值)状态编译的程序一旦发生I/O错误时将会举发一个EInOutError的例外假如我们在特定的情况下不希望出现这个例外的讯息时(例如前文提到的侦测档案是否存在函数), 可以将这个Compiler directive设为{$I-}, 此时程序执行时是否发生过错误,程序设定师必须自行检查IOResult这个公用变量的值如果是零表示没有错误非零的错误代码含意请详查Online help
        {$L文件名称连结目标文件
        如果您有一个.OBJ文件要并入Delphi的程序时可以在程序中加入:{$L OTHER.OBJ} .这样就可以使用OTHER.OBJ中的程序了值得注意的是函数或程序在呼叫前仍然必须用external宣告过表明这些模块是来自「外部」的函式。举例来说笔者有一份由Keypro厂商提供的.OBJ在使用时相关的程序如下:

{$L hasptpw.obj}
{$F+}
procedure hasp (Service, SeedCode, LptNum, Pass1, Pass2 : word;
                var p1,p2,p3,p4 : word); external;
{$F-}

        经过{$L hasptpw.obj}宣告之后程序的其它部分就可以直接呼叫原先位于 hasptpw.obj中的hsap这个程序了。
        {$L+} 区域符号信息
        {$L+}, Delphi会额外加入一些区域符号信息这使得我们可以应用Delphi IDE中的View/Call Stack, View/Watch在程序执行时检视变量内容与函式呼叫的关系。
        应用这个Compiler directive的注意事项有:
         {$D-}, {$L+}不会有作用。
         使用{$L+}, 只会加大.DCU的档案大小.EXE的大小与执行速度并没有影响。
         {$H+} 长字符串宣告
         好用的长字符串不仅没有资料长度255的限制C语言惯用的Null-terminated string兼容性也大为提高。
         使用{$H}时的注意事项有:
         {$H+}的编译情形下string定义的字符串变量都是长字符串请注意字符串是否为长字符串是在字符串定义时决定的例如:
procedure TForm1.Button1Click(Sender: TObject); 
{$H-}
var
  s: string;
begin
{$H+}
  s := '测试一下长字符串';
  Windows.MessageBox(0, pchar(s), '讯息', 64);
end;
        由于var{$H-}的缘故虽然在begin后我们立即设定为{$H+}, s仍然是一个短字符串所以自然不能像是长字符串一样pchar强制型别转换后当作Null-terminated字符串使用。
        承上不管程序是{$H+}{$H-}, 只要字符串是以长字符串方式定义的即使begin..end;中改成{$H-}, 该字符串的操作仍然具有长字符串的特性。
        因此由于VCL中的字符串都是长字符串即使我们的程序是{$H-}, 仍然可以拿它们当长字符串来使用。
         不论{$H}的状态如何AnsiString定义的一定是长字符串string[n]ShortString定义的一定是短字符串。
        {$M 16386, 1048576} 内存配置大小
         要改变唯叠(Stack)内存配置大小时我们可以有以下两种选择:
         使用{$MINSTACKSIZE数字}, {$MAXSTACKSIZE数字}, 分别指定最小.最大的Stack大小或者使用{$M min, max}, 同时指定最小与最大的值。
         使用这些Compiler directive时的注意事项有:
          1,写在.DPR中才有效果。
          2, 堆栈的最小数字必须介于102421474835647之间。
          3, 堆栈的最大数字必须介于$MINSTACKSIZE21474835647之间。
          4, 当内存不足而无法满足最小的堆栈大小时, Windows会在激活这程序时提出错误报告。
          5, 当程序要求的内存超过$MINSTACKSIZE的大小时将举发EStackOverflow例外。
        {$Z1} 最小列举大小
        这个Compiler directive将影响储存列举型态时最小所需的byte数值。如果宣告列举型态时数值不大于256,而且也在系统预设的{$Z1}这个列举型态只占用一个byte储存的。{$Z2}以两个byte储存, {$Z4}以四个byte储存。因为C语言通常以WORDDWORD储存列举型态如果您的程序需要与CC++沟通时,{$Z2}{$Z4}就很管用了
{$Z+}, {$Z-}分别对应到{$Z1}{$Z4}
       {$P+} 开放字符串参数
       在程序与函数宣告时其中的字符串自变量{$P+}时表示是Open string; {$P-},只是一般的字符串变量而已。这个Compiler directive只在{$H-}时有作用。
       {$O+} 最佳化开关
        建议您维持{$O+}的系统默认值。开启这个Compiler directive, Delphi会自动进行最佳化处理程序可以因此跑得快一些您可以放心的打开这个编译开关, Delphi不会进行不安全的最佳化而使您的程序执行时发生错误。
        {$Q-} 满溢检查, {$R-} 范围检查
        {$Q}{$R}是一组搭配使用的Compiler directive, 它们将检查数值或数组的操作是否在安全的边界中, {$Q}会检查整数运算(+, -, Abs, Sqr, Pred, Succ), {$R}则检查字符串与数组的存取是否超出合理边界范围等问题。
        使用这两个Compiler directives会因为这些检查动作而降低程序执行的速度通常我们会在除错时开启这两个编译开关。
        {$U-} Pentium CPU浮点运算安全检查
        还记得早期Pentium CPU浮点运算不正确的事吧这批CPU应该回收得差不多了但如果您仍然不确定程序会不会意外的遇到漏网之鱼或黑心牌经销商的话请将这个Compiler directives设为{$U+}
        根据Borland手册的说明如果CPU是没有暇疵的设定{$U+}对于执行速度只有轻微的影响但如果是问题CPU, 浮点的除法速度会因此慢上三倍是否要打开这个开关您心中应该已有取舍。
         {$R文件名称资源档
         在您还没有开始学习Compiler directives之前这个指令就已经出现在您的程序中了,每次开出一个新的form, Delphi自动在Implement开头部分中加入{$R *.DFM}, Project/Source中看到的.DPR程序中也有{$R *.RES},这些是什么意思呢意思是说在编译连结时含入与项目主档名同名的.RES, 以及与form unit档案同名的.DFM等资源档。
         如果您需要在程序中使用额外的资源(例如自订鼠标指针), 请注意不要自行以Resouse WorkShopImage Editor等资源编辑器更改这些与ProjectForm同名的资源档改变这些同名的档案不仅无效可能还有不可预期的错误。因些您应该在另外一个资源档中存放这些资源并于{$R}中写明档案的名称将其连结进来例如:{$R MyCursor.res}
       {$T-} @指针型态检查
        应用@操作数可以取得变量的地址{$T-}@取得是一个无型别的指针(Pointer)。反过来说{$T+},是有型别的指针假定I是一个integer的变量, @I所得到的即是相当于^Integer(Pointer of Integer)的指针。
        {$WARNINGS ON} 编译器警告
        这个Compiler directive{$HINTS}的作用类似同样会对程序的可能问题提出警告。不同的是,{$WARNINGS ON}, Compiler会对未初始化的变量、没有传回值的函数、建构抽象对象等情况提出警告。
        {$J-} 型态常数只读
        从前笔者曾经对以下的程序产生过疑惑:
{$J+}
procedure TForm1.Button1Click(Sender: TObject);
const
  VarConst: integer = 4;
begin
  VarConst := 5;
  ShowMessage(IntToStr(VarConst));
end;
        const不是常数吗为什么可以改呢在先前的Pascal版本中const VarName: DataType = const value;
定义的具型态常数的确是可以改的假如您希望常数就是常数它不应该允许修改请将这个Compiler directive设为{$J-}不论是{$J+}{$J-}, const VarName = const value; 定义的常数(没有加上型别宣告), 是一个真正的常数其它的程序不可以改变其内容。其实{$J+}时还有一个妙用那就是宣告出类似C语言static的变量换句话说产生了一个与Application相同生命周期的变量。在这种情形下变量只在第一次使用时才会建立函数或程序结束时该变量也不会消灭下一次再呼叫到这个函数或程序时我们仍然可以参考到上次执行结束时的值。让我们试一下这个例子:
{$J+}
procedure TForm1.Button1Click(Sender: TObject);
const
  i: integer = 0;
begin
  ShowMessage(IntToStr(i));
  Inc(i);
  ShowMessage(IntToStr(i));
end;
        第一次执行时我们分别会看到「0」「1再点一次这个按钮时看到的将是「1」「2」。

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值