程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Delphi >> Delphi 異常處理與程序調試(2)

Delphi 異常處理與程序調試(2)

編輯:Delphi

 使用自定義異常需要:

  1.自己定義一個異常對象類;

  2.自己引發一個異常。 

  12.3.5.1 定義異常對象類   異常是對象,所以定義一類新的異常同定義一個新的對象類型並無太大區別。由於缺省異常處理只處理從Exception或Exception子類繼承的對象,因而自定義異常類應該作為Exception或其它標准異常類的子類。這樣,假如在一個模塊中引發了一個新定義的異常,而這個模塊並沒有包含對應的異常響應,則缺省異常處理機制將響應該異常,顯示一個包含異常類名稱和錯誤信息的消息框。

  下面是一個異常類的定義: 

  type

  EMyException = Class(Exception) ; 

  12.3.5.2 自引發異常 

  引發一個異常,調用保留字raise,後邊跟一個異常類的實例。

  假如定義: 

  type

  EPassWordInvalid = Class(Exception); 

  則在程序中如下的語句將引發一個EPassWordInvalid異常: 

  If Password <> CorrectPassWord then

  raise EPasswordInvalid.Create('Incorrect PassWord entered');

  異常產生時把System庫單元中定義的變量ErrorAddr的值置為應用程序產生異常處的地址。在你的異常處理過程中可以引用ErrorAddr的值。

  在自己引發一個異常時,同樣可以為ErrorAddr分配一個值。

  為異常分配一個錯誤地址需要使用保留字at,使用格式如下: 

  raise EInstance at Address_Expession;

  12.3.5.3 自定義異常的應用舉例  

  下面我們給出一個利用自定義異常編程的完整實例。

  兩個標簽框(Label1、Label2)標示對應編輯框的功能。編輯框PassWord和InputEdit用於輸入口令和數字。程序啟動時Label2、InputEdit不可見。當在PassWord中輸入正確的口令時,Label2、InputBox出現在屏幕上。此時Label1、PassWord隱藏。

 設計時,令Label2、InputEdit的Visible屬性為False。通過設置PassWord的PassWordChar可以確定輸入口令時回顯在屏幕上的字符。

  自定義異常EInvalidPassWord和EInvalidInput分別用於表示輸入的口令非法和數字非法。它們都是自定義異常EInValidation的子類。而EInValidation直接從Exception異常類派生。

  下面是三個異常類的定義。 

  type
EInValidation = class(Exception)
public
ErrorCode: Integer;
constructor Create(Const Msg: String;ErrorNum: Integer);
end;
EInvalidPassWord = class(EInValidation)
public
constructor Create;
end;
EInvalidInput = class(EInValidation)
public
constructor Create(ErrorNum: Integer);
end;
 

  EInValidation增加了一個公有成員ErrorCode來保存錯誤代碼。錯誤代碼的增加提供了很大的編程靈活性。對於異常類,可以根據錯誤代碼提供不同的錯誤信息;對於使用者可以通過截取錯誤代碼,在try...except模塊之外來處理異常。

  從以上定義可以發現:EInvalidPassWord和EInvalidInput的構造函數參數表中沒有表示錯誤信息的參數。事實上,它們保存在構造函數內部。下面是三個自定義異常類構造函數的實現代碼。 

  constructor EInValidation.Create(Const Msg: String; ErrorNum: Integer);
begin
inherited Create(Msg);
ErrorCode := ErrorNum;
end;
constructor EInValidPassWord.Create;
begin
inherited Create('Invalid PassWord Entered',0);
end;
constructor EInValidInput.Create(ErrorNum: Integer);
var
Msg: String;
begin
case ErrorNum of
1:
Msg := 'Can not convert String to Number';
2:
Msg := 'Number is out of Range';
else
Msg := 'Input is Invalid';
end;
inherited Create(Msg,ErrorNum);
end;
 

對於EInvalidInput,ErrorCode=1表示輸入的不是純數字序列,而ErrorCode=2表示輸入數值越界。

  口令檢查是用戶在PassWord中輸入口令並按下回車鍵後開始的。實現代碼在PassWord的OnKeyPress事件處理過程中: 

  procedure TForm1.PassWordKeyPress(Sender: TObject; var Key: Char);
const
CurrentPassWord = 'Delphi';
begin
if Key = #13 then
begin
try
if PassWord.text <> CurrentPassWord then
raise EInvalidPassWord.Create;
Label2.Visible := True;
InputEdit.Visible := True;
InputEdit.SetFocus;
PassWord.Visible := False;
Label1.Visible := False;
except
on EInvalidPassWord do
begin
PassWord.text := '';
raise;
end;
end;
Key:=#0;
end;
end;
 

  同樣,在InputEdit的OnKryPress事件處理過程中實現了輸入數字的合法性檢查: 

  procedure TForm1.InputEditKeyPress(Sender: TObject; var Key: Char);
var
Res: Real;
Code: Integer;
begin
if Key = #13 then
begin
try
val(InputEdit.text,Res,Code);
if Code <> 0 then
raise EInValidInput.create(1);
if (Res > 1) or (Res < 0) then
raise EInValidInput.create(2);
MessageDlg('Correct Input', mtInformation,[mbOk], 0);
Key := #0;
except
on E:EInValidInput do
begin
InputEdit.text := '';
MessageDlg(E.Message, mtWarning,[mbOk], 0);
end;
end;
end;
end;
 

  由於異常響應後即被清除,所以要顯示異常信息,需要另外的手段。在以上兩段程序中我們采用了兩種不同的方法:在口令合法性檢查中,利用異常重引發由系統進行缺省響應;在輸入數字合法性檢查中,通過異常實例來獲取異常信息並由自己來顯示它。

  以上所舉的是一個非常簡單的例子,但從中已可以發現:使用自定義異常編程,為程序設計帶來了很大的靈活性。 

  12.3.6 利用異常響應編程 

  利用異常處理機制不僅能使程序更加健壯,而且也提供了一種使程序更加簡捷、明了的途徑。事實上,使用自定義異常類就是一種利用異常響應編程的方式。這裡我們再討論幾個利用標准異常類編程的例子。

  比如為了防止零作除數,可以在進行除法運算前使用if…then…else語句。但如果有一系列這樣的語句則繁瑣程度是令人難以忍受的。這時候我們可能傾向於使用EDivByZero異常。例如如下一段程序就遠比用if…then…else實現簡捷明了。 

  function Calcu(x,y,z,a,b,c:Integer):Real;
begin
try
Result := x/a+y/b+z/c ;
except
on EDivByZero do
Result := 0;
end;
end;

  在(6.2.3)記錄文件的打開與創建中就是利用異常響應來實現文件的打開或創建。 

  procedure TRecFileForm.OpenButtonClick(Sender: TObject);
begin
if OpenDialog1.Execute then
FileName := OpenDialog1.FileName
else
exit;
AssignFile(MethodFile,Filename);
try
Reset(MethodFile);
FileOpened := True;
except
on EInOutError do
begin
try
if FileExists(FileName) = False then
begin
ReWrite(MethodFile);
FileOpened := True;
end
else
begin
FileOpened := False;
MessageDlg('文件不能打開',mtWarning,[mbOK],0);
end;
except
on EInOutError do
begin
FileOpened := False;
MessageDlg('文件不能創建',mtWarning,[mbOK],0);
end;
end;
end;
end;
if FileOpened = False then exit;
Count := FileSize(MethodFile);
if Count > 0 then
ChangeGrid;
RecFileForm.Caption := FormCaption+' -- '+FileName;
NewButton.Enabled := False;
OpenButton.Enabled := False;
CloseButton.Enabled := True;
end;
 

 總之,利用異常響應編程的中心思想是雖然存在預防異常發生的確定方法,但卻對異常的產生並不進行事前預防,而是進行事後處理,並以此來簡化程序的邏輯結構。 

  12.4 程序調試簡介   Delphi提供了一個功能強大的內置調試器(Integrated Debugger), 因而對程序的調試不用離開集成開發環境(IDE)就可以進行。

  程序錯誤基本可以分為兩類,即運行時間錯和邏輯錯。所謂運行時間錯是指程序能正常編譯但在運行時出錯。邏輯錯是指程序設計和實現上的錯誤。程序語句是合法的,並順利執行了,但執行結果卻不是所希望的。

  對於這兩類錯誤,調試器都可以幫助你快速定位錯誤,並通過對程序運行的跟蹤和對變量值的監視幫助你尋找錯誤的真正原因和解決錯誤的途徑。

  程序調試的主要內容可以概括為如下的幾方面:

  1.調試的准備和開始;

  2.控制程序的執行;

  3.斷點的使用;  4.檢查數據的值。

  程序調試只有用戶實際上機操作才能真正掌握。在這一節中我們主要對調試中的主要問題和一些關鍵點進行介紹。至於一些很細小的問題相信讀者可以在上機實際應用中掌握,因而沒有列出。

  12.4.1 調試的准備和開始 

  在程序開發過程中程序編碼和調試是一個持續的循環過程,只有在你對程序進行了徹底的測試後才能交付最終用戶使用。為了保證調試的徹底性,在調試前應制定一個詳細的調試計劃。一般說來應該把程序劃分為幾個相對獨立的部分,分別進行調試,以利於錯誤的迅速定位,確保每一部分程序都按設計的要求運行。

  調試計劃准備好後就可以開始程序的調試。

  開始一個調試過程包括:

  1.編譯時產生調試信息;

  2.從Delphi裡運行你的程序。

  在程序調試過程中,程序的執行完全在你的控制之中。你可以在任何位置暫停程序的執行去檢查變量和數據結構的值,去顯示函數調用序列,去修改程序中變量的值以便觀察不同值對程序行為的影響。 

 12.4.1.1 產生調試信息 

  要使用內部調試器必須選中Option| Environment菜單References頁的Integrated Debugging檢查框。缺省情況下該框被選中。

  在開始調試前需要使用Symbols Debug Information(調試符號信息)編譯工程文件。調試符號信息包含了一個符號表,能夠使調試器在程序的源代碼與編譯器產生的機器代碼間建立聯系。這樣在程序執行中可以同時查看對應的源代碼。

  Delphi 在缺省情況下自動產生調試符號信息。在集成開發環境中的開關選項是Option|project菜單Compiler Options頁的Debug Information and Local Symbols檢查框。

  當產生的調試符號信息供內部調試器使用時,編譯器把調試符號表儲存在每個相應的.dcu文件中。

  如果希望在集成環境外使用Turbo Debugger,則需要把調試信息儲存在最終的 .exe文件中。為此需要選定Option|Project菜單Linker頁的Include TDW Debug Info檢查框。

  由於儲存調試信息大大增加了執行文件的大小,因而調試完成後應重新生成一個不包含調試信息的執行文件。 

  12.4.1.2 運行程序 

  通過調試器(包括內置調試器)運行程序,當程序處於等待狀態時,調試器可以獲得控制,利用調試器的功能來檢查當前程序的狀態。通過合理布置屏幕顯示,使應用程序運行窗口和Code Editor(代碼編輯器)互不重疊,可以讓用戶在它們間方便地切換以觀察代碼執行的效果。

  如果希望使用命令行參數來調試程序,則可以通過Run|Parameters 菜單打開運行參數對話框進行設置。 

  12.4.2 程序運行的控制 

  程序運行控制的方法和使用如下表。 

  表12.7  程序運行控制的方法和使用途徑

  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

方法 使用途徑

  ───────────────────────────────

  運行到光標位置 ● Code Editor加速菜單的Run to Cursor項

  (Run to Cursor) ● Run主菜單的Run to Cursor項

  ● F4

  跟蹤(Trace Into) ● Run主菜單的Trace Into項

  ● Trace Into加速按鈕

  ● F7

  步進(Step Over) ● Run主菜單的Step Over項

  ● Step Over加速按鈕

  ● F8

  運行到斷點 設置斷點並按正常方式運行暫停程序執行 Run主菜單的Program Pause項

  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 

  跟蹤和步進都是一種單步執行方式。但“步”的含義不同。對跟蹤而言它一次執行一條簡單程序語句。當碰到包含調試信息的函數或過程調用時則跳入該函數或過程,並執行其第一條可執行語句。對步進而言它一次執行一條當前模塊的可執行語句,而不管該語句是否是函數或過程調用。

  運行到光標位置和運行到斷點都是程序正常運行到某一確定的源代碼位置,而後進入調試狀態。但相對於運行到光標位置而言,運行到斷點更為靈活。因為斷點一次可設置多個,同時也可以對斷點設置一定的條件。只有滿足該條件程序運行才會中止。

  12.4.3 斷點的使用 

  12.4.3.1 設置斷點 

  設置斷點首先在Code Editor中選定你想設置斷點的代碼行,而後進行如下的任一種操作:

  ● 單擊選定代碼行左邊的空白

  ● 按F5

  ● 選擇Code Editor加速菜單的Toggle BreakPoint項

  ● 選擇Run|Add Breadpoint打開斷點編輯對話框(Edit BreakPoint Dialog Box),而後選擇New去確認一個新的斷點設置或選擇Modify去對一個存在的斷點進行修改

 ● 從BreakPoint List加速菜單中選擇Add BreakPoint項 

  斷點必須位於可執行代碼行上,凡設置在注釋、空白行、變量說明上的都是無效的。另外,斷點既可以在設計狀態下設置也可以在運行調試狀態下設置。 

  12.4.3.2 斷點的操作 

  斷點列表窗口(BreakPoint List Window)列出了所有斷點所在的源文件名、行號、條件以及已通過的次數。如果一個斷點非法或失去功能,則在列表窗口中變灰。

  斷點列表窗口可以通過選擇VIEw|BreakPoint菜單打開。

  斷點列表窗口是斷點操作的基礎。

  1.顯示和編輯斷點處的代碼

  利用斷點列表窗口可以快速找到斷點在源代碼中的位置。

  首先選定斷點而後從加速菜單中選擇View Source或Edit Source。此時Code Editor更新,顯示該斷點位置處的代碼。如果選擇的是VIEw Source,則斷點列表窗口仍保持活動;如果選擇的是Edit Source,則Code Editor獲得輸入焦點,可以在斷點位置修改源代碼。  2.斷點功能的喪失和恢復

  使斷點失去功能可以使斷點從當前程序運行中隱藏起來。假如你定義了一個斷點當前並不需要,但可能在以後使用,則這一功能是很有用的。

  斷點列表窗口加速菜單的Disable BreakPoint和Disable All BreakPoints項可以使當前選中斷點或所有斷點失去功能。

  加速菜單中的Enable BreakPoint和Enable All BreakPoint 可以使相應斷點恢復功能。

  3.斷點的刪除

  斷點刪除可以從Code Editor或斷點列表窗口中進行。

  從Code Editor:

  ● 把光標停到包含斷點的行並按F5(或選擇加速菜單的Toggle BreakPoint)

  ● 單擊包含斷點行左邊的終止符 

  從斷點列表窗口:

  ● 選中欲刪除的斷點並選擇加速菜單的Delete BreakPoint項


● 刪除當前所有斷點,則選擇加速菜單的Delete All BreakPoints項 

  12.4.3.3 修改斷點屬性 

  斷點列表窗口雙擊選定斷點或從加速菜單中選擇Edit BreakPoint項,可以打開斷點編輯對話框,用於顯示和修改斷點的屬性。

  利用斷點編輯對話框可以改變斷點的位置,設置斷點條件。

  斷點條件包括兩種:布爾表示式和通過次數。

  Condition編輯框用於設置布爾表達式條件。如果表達式值為真(或非零)則程序運行在斷點處中止;否則調試器將忽略該斷點。

  Pass Count編輯框用於設置通過次數條件,即只有當程序運行在該斷點處通過設定次數時程序運行才在該斷點處中止。這往往用於對循環體內語句的調試。

  有一點應引起注意的是:當Condition和Pass Count同時設置時,Pass Count是指滿足條件的通過次數。

  對如下一段程序: 

  var
i,Re,s: Integer ;
begin
s := 1;
Re := 0;
for i:=1 to 100 do
Re:=Re+s*i ;
end;
 

  在 Re := Re + s*i; 一行設置一斷點。

  若條件設置為: 

  Condition :  i = 3

  Pass Count:   4

  則當程序中止時檢測i 的值為7。 

  12.4.3.4 斷點和程序執行點顏色的設置 

  選擇Option|Environment進入環境設置對話框而後選擇Editor Colors頁標簽。此時即可對有關項按自己的希望設置背景和前景色。 

  12.4.4 監視數據的值 

  內置調試器提供了如下的工具用於監視程序中數據的值:

  ● 監視列表窗口

  ● 計算/修改對話框

  ● 調棧窗口 

  12.4.4.1 監視表達式 

  監視列表窗口(Watch List Window)顯示程序運行中當前監視表達式的值。


 選擇VIEw|Watches可以打開監視列表窗口。

  從Code Editor中添加一個監視表達式最方便的方法是:

  1.選中要監視的表達式;

  2.從Code Editor加速菜單中選擇Add Watch把表達式添加到監視列表窗口。

  也可以利用下面的方法產生一個監視表達式:

  1.用下列方法之一打開監視屬性對話框(Watch PropertIEs Dialog Box):

  ●主菜單中選擇Run|Add Watch

  ●在光標處從Code Editor加速菜單中選擇Add Watch

  ●按Ctrl-F5

  ●雙擊監視列表窗口中的一個監視表達式

  ●從監視列表窗口選定一個表達式而後從加速菜單中選擇Edit

  2.在監視屬性對話框的Expression下拉列表框中輸入或選擇一個被監視的表達式;

  3.設定表達式的顯示格式和使能狀態。

  與斷點類似,利用加速菜單也可以使監視表達式功能喪失、恢復或刪除監視表達式。 

  12.4.4.2 計算/修改表達式 

  選擇Run|Evaluate /Modify可打開計算/修改對話框。當單擊Evaluate按鈕時,Expression編輯框中表達式的值顯示在Result域中。

  Expression中可以輸入或選擇任何合法的表達式(包括對象的屬性),但不包括;

  1.包含有當前執行點不能引用的局部或靜態變量的表達式;

  2.函數或過程調用。

  Expression中的表達式可以帶特定的格式字符用於規定其顯示格式。 格式字符及其功能如下表。 

  表12.8  格式字符及其功能

  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  格式字符 功 能

  ─────────────────────────────────

  $,H,X 以十六進制格式顯示標量

  D 以十進制格式顯示標量

C 把ASCII碼在0..31的特殊字等顯示為ASCII碼圖形

  Fn 用n個有效數字顯示浮點數

  M 以十六進制方式顯示一變量的內存轉儲值

  P 以段和偏移量格式顯示指針。兩部分皆為四位十六進制值

  R 顯示記錄、對象的域名和值(例如 X:5,Y:2)

  S 用ASCII碼顯示字符串(包括特殊字符)。用於修改內存轉儲值

  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  修改表達式的值常用於驗證錯誤解決方案的正確性。在Expression編輯框中輸入或選定欲修改的表達式,單擊Evaluate按鈕觀察表達式的當前值。而後在New Value編輯框中輸入或選中一個新值,並單擊Modify按鈕確認並更新數據項。這種修改只影響特定的程序運行。

  修改表達式的值(特別是指針變量和數組下標)可能會引起無法預計的後果。因而使用中要特別小心。 

  12.4.4.3 顯示函數調用 

  選擇VIEw|Call Stack可以顯示調棧窗口(Call Stack Window)。調棧窗口的頂端列出了應用程序最近的函數調用。

  利用調棧窗口可以退出當前跟蹤的函數,可以利用加速菜單項顯示或編輯位於特定函數調用處的源代碼

  12.5 其它調試工具 

  Delphi的內置調試器雖然功能很強大,但並不能勝任所有的任務。同時由於內置調試器在執行中引起程序環境的細微變化,所以可能影響錯誤的發生方式。為此我們需要使用其它調試工具來完成我們的任務。這些調試工具包括Turbo Debugger、WinSight、WinSpector和Browser。Browser將在下一節中專門進行介紹。 

  12.5.1 Turbo Debugger 

  Turbo Debugger是Borland公司推出的第三代語言調試器,它雖然還沒有推出完全支持Delphi的新版本,但也基本能勝任一般Delphi程序的調試。

 Turbo Debugger在字符模式下執行,但它是一個真正的Windows程序,它僅使用基於字符的界面。由於Turbo Debugger是一個准備控制其它程序的特殊程序,因此不可以使用通常Windows任務切換功能如Alt+Tab。

  Turbo Debugger的操作大部分與內置調試器相同或類似。利用File|Open菜單裝入要調試的文件就可以開始一個調試過程。

  利用Turbo Debugger必須把調試符號信息儲存在可執行文件中。具體操作見(12. 4.1.1)中介紹。

  Turbo Debugger與內置調試器相比,有許多新的功能。

  首先它提供了許多在低級硬件信息方面的控制。可以完全訪問CPU的寄存器、標志及系統內存。用戶可以跟蹤遠指針到內存位置並直接檢查它們的內容。Turbo Debugger可以跟蹤進到代碼中,即使得不到源代碼也可以。

  Turbo Debugger支持許多Windows的特殊功能。它可以跟蹤Windows消息,讓用戶查看程序的局部堆和全局堆,並可以浏覽包括DLLs在內的組成程序的所有代碼單元列表。

  另外Turbo Debugger支持遠程執行能力。可以通過串口鏈接或通過支持NetBiOS的網絡配置Turbo Debugger控制另外一台機器。在這種模式下,一台機器顯示調試器屏幕,另一台機器顯示被跟蹤的程序。這允許在一個屏上單步執行程序並在另一屏上監視結果。 

  12.5.2 WinSight

  WinSight 是一個用於查看Windows 對象並跟蹤消息的發送和接收的調試工具。WinSight的圖標可以在Delphi程序組中找到。

  WinSight界面分為兩部分,上面為對象樹窗口,下面為消息跟蹤窗口。如圖12.9所示。

  在實際應用中,用戶可能只是對其中的一部分消息感興趣,而又不希望它們淹滅在無用信息之中。為此,用戶可以打開Message菜單並選擇Selected Windows。按住Shift鍵,單擊對象樹窗口中感興趣的對象,所選定對象的任何消息都顯示在消息跟蹤窗口中。

  如果用戶只想跟蹤某些消息類,則打開Message菜單並選擇Options ,使用如圖12.10所示的檢查框過濾消息。 

  12.6.6 小結 

  本章介紹的內容,核心是如何增強程序的健壯性並提高開發效率。為此我們首先考察了Delphi的異常處理機制,而後介紹了幾種程序調試工具,在您即將結束基礎篇的學習時,這些內容是您步入開發大型應用程序的高級程序員行列的必備武器。









  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved