1. 程式人生 > >Delphi的編譯指令

Delphi的編譯指令

一個程式從無到有的過程是這樣的: 編輯程式碼 -> 預處理 -> 編譯(成dcu等) -> 連結(為exe等).
一、什麼是預處理?
譬如 VCL 中有很多程式碼是相容 Linux 的, 在 Windows 下就需要在編譯之前預處理掉那些 for Linux 的程式碼.
1、判斷作業系統: 其中的 "MSWINDOWS" 和 "LINUX" 就是 Delphi 預定義的 "條件識別符號".
begin 
  {$IFDEF MSWINDOWS} 
    ShowMessage('Windows'); 
  {$ENDIF} 
 
  {$IFDEF LINUX} 
    ShowMessage('Linux'); 
  {$ENDIF} 
end;

2、自定義條件識別符號(DEFINE): 下面例子中自定義了條件識別符號: Nobird; 識別符號和定義它的指令都不區分大小寫, 但大家一般慣用大寫.
begin 
  {$DEFINE Nobird} 
  {$IFDEF Nobird} 
    ShowMessage('識別符號 Nobird 已定義'); 
  {$ELSE} 
    ShowMessage('識別符號 Nobird 未定義'); 
  {$ENDIF} 
end; 

3、取消條件識別符號的定義(UNDEF):
begin 
  {$DEFINE Nobird} 
  {$IFDEF Nobird}  
    ShowMessage('確認識別符號 Nobird 是否定義'); 
  {$ENDIF} 
 
  {$UNDEF Nobird} 
  {$IFDEF Nobird}  
    ShowMessage('再次確認識別符號 Nobird 是否定義'); 
  {$ENDIF} 
end; 

4、取消定義的簡單辦法: 在 {$...} 的 $ 前面隨便加點什麼, 讓它變成 "註釋", 譬如: {.$}
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.
begin 
  {$IFDEF DEBUG} 
    ShowMessage('除錯模式'); 
  {$ENDIF} 
 
  {$IFDEF RELEASE} 
    ShowMessage('釋出模式'); 
  {$ENDIF} 
end;

7、編譯指令寫在哪?: 編譯指令可以寫在內碼表的任何地方, 不過在程式碼的不同區域有時也會不同;
譬如: {$APPTYPE GUI} 和 {$APPTYPE CONSOLE} 就只能寫在工程檔案裡才有效.
{$APPTYPE GUI} 和 {$APPTYPE CONSOLE} 分別表示視窗工程和控制檯工程.
其中 {$APPTYPE GUI} 是預設的, 所以很少見到它.
它甚至可以嵌入到程式碼行當中, 譬如 ActnColorMaps 單元就有這麼一句:
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 , 然後看看最上面...
下面列出了這些預設設定:
{$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 的標識.
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 的標識.
begin 
  {$IFNDEF VER150} 
    ShowMessage('這不是 Delphi 7'); 
  {$ENDIF} 
 
  {$IF NOT DEFINED(VER150)} 
    ShowMessage('這不是 Delphi 7'); 
  {$IFEND} 
end; 

3. 可以使用 or 和 and:
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 單元裡的很多常量都沒問題.
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 次.
begin 
  {$IFOPT B+} 
    ShowMessage('指令 B 已開啟'); 
  {$ELSE} 
    ShowMessage('指令 B 已關閉'); 
  {$ENDIF} 
 
  {$B+} 
  {$IFOPT B+} 
    ShowMessage('Ok!'); 
  {$ENDIF} 
end;
指令及預設值 可選值 範圍 註釋 舉例
{$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}