VB==容錯處理策略(全) (轉)

worldblog發表於2007-12-04
VB==容錯處理策略(全) (轉)[@more@]

 


  本文將詳細地討論在中處理錯誤發生的On Error命令家族,它們的任務就是錯誤並解決錯誤的發生,並解釋錯誤程式碼的含義。閱讀完本文後,你將能編寫基本的錯誤代理,有效地防止以外錯誤。 作者:甘冀平 2000-11-03

   本文包括以下幾節內容:

  • 使用On Error語句
  • 如何離開錯誤處理程式
  • 定義錯誤常數
  • 將錯誤處理程式單獨存放
  • 理解錯誤處理的範圍
  • 不要巢狀使用錯誤處理程式
   使用On Error語句

   Visual Basic程式使用On Error命令來登記錯誤處理程式,它有下面3種形式:

  • On Error GoTo 0
  • On Error Resume Next
  • On Error GoTo line

   These forms tell Visual Basic what it should do when the program encounters an error. The three forms are described in the following sections.

   這些程式碼告訴Visual Basic程式當遇到錯誤時應該做什麼。下面詳細介紹這3種語句。

   On Error GoTo 0

   On Error GoTo 0比較直接,它僅僅中止任何當前安裝的錯誤處理代理(比如在前面安裝的On Error GoTo line或者On Error Resume Next語句)。如果程式在其後遇到錯誤,那麼就會崩潰。

   On Error Resume Next

   這種方法使程式忽視發生的錯誤,當遇到一個錯誤時,程式繼續在其後。如果程式使用了On Error Resume Next命令,那麼就應該在每次可能產生錯誤的操作後面檢查一下Err的數值。如果Err.Number不為零,就表明操作產生了錯誤,程式可以對此採取特殊的動作。注意:在可能發生問題的語句後,程式應該立即檢查Err.Number的數值,因為,其他的錯誤操作將復位Err物件並清除前面的錯誤資訊。

   許多開發人員在給一個通用對話方塊後使用On Error Resume Next命令。 CommandDialog的CancelError屬性指明瞭當使用者Cancel對話方塊時是否產生一個錯誤。下面的程式碼段顯示了程式如何使用CancelError判斷是否繼續一個操作,比如裝入一個。

' 如果使用者選擇了cancel,就產生一個錯誤 dlgOpenFile.CancelError = True ' 忽視錯誤的發生 On Error Resume Next ' 顯示對話方塊 dlgOpenFile.ShowOpen ' 判斷是否有錯誤 If Err.Number = cdlCancel Then ' 如果是使用者canceled,就什麼也執行 Exit Sub ElseIf Err.Number <> 0 Then ' 如果是未知錯誤,就採取另外的行為 : End If ' 恢復正常的錯誤處理 On Error GoTo 0


   On Error GoTo Line

   使用這個方法,將登記一個新的錯誤處理代理。如果程式遇到錯誤,控制將轉移到定義的程式行,然後再執行特殊的處理。

   請看下面的程式碼:         

Private Sub DoSomething() ' 安裝錯誤處理代理 On Error GoTo UnexpectedError ' 日常操作 : ' 使程式不進入下面的錯誤處理代理程式 Exit Sub UnexpectedError: '將錯誤資訊描述給使用者 MsgBox "Unexpected error" & _ Str$(Err.Number) & _ " in subroutine DoSomething." & _ vbCrLf & _ Err.Description Exit Sub End Sub


 

  如何離開錯誤處理程式

   有下面幾種方法可以使程式離開錯誤處理代理程式的控制,然後返回到正常的執行程式碼處:

  • Resume
  • Resume Next
  • Exit Sub/Function/Property
  • End Sub/Function/Property
  • Err.Raise
   下面對它們進行詳細地描述。   

   Resume

   執行Resume語句後,將重複執行發生錯誤的那條命令。如果那條命令仍然不正確,程式將再次發生錯誤,這將使程式進入一個不停的迴圈操作中。為了避免這種迴圈發生,一般不要使用Resume命令,除非在錯誤處理代理程式中能對錯誤進行修復。

   例如,下面的程式碼試圖裝入一個可能在上地檔案。如果失敗,將報告錯誤,詢問使用者是否再試一試。如果不在軟碟機中,使用者可以將之插入然後點選Retry按鈕。這裡的程式就是使用了Resume命令實現再次開啟檔案。當程式又一次失敗時,就會再次轉到錯誤處理代理程式中,給使用者再次修正錯誤的機會。最終,直到使用者修正了錯誤,或者點選了Cancel按鈕。如果點選了Cancel按鈕,程式退出,沒有開啟檔案。   

Private Sub LoadData(ByVal filename As String) Dim fnum As Integer '開啟檔案 fnum = FreeFile On Error GoTo OpenError Open filename For Input As fnum ' 讀取資料 On Error GoTo ReadError : ' 關閉檔案 On Error GoTo CloseError Close fnum Exit Sub OpenError: ’開啟檔案失敗,詢問使用者是否再次開啟 If MsgBox("Error" & _ Str$(Err.Number) & _ " opening file " & filename & "." & _ vbCrLf & Err.Description & vbCrLf & _ "Check that the disk is proy " & _ "inserted and click the Retry button.", _ vbRetryCancel, _ "Error opening file") = vbRetry _ Then '再次開啟檔案 Resume End If ’否則,選擇Cancel退出這個過程 Exit Sub ReadError: MsgBox "Error" & _ Str$(Err.Number) & _ " reading file " & filename & "." & _ vbCrLf & Err.Description '關閉檔案 Close fnum Exit Sub CloseError: ' 關閉檔案發生錯誤 MsgBox "Error" & _ Str$(Err.Number) & _ " closing file " & filename & "." & _ vbCrLf & Err.Description Exit Sub End Sub


 

  Resume Next

   執行Resume Next命令使程式在發生錯誤那條命令後繼續執行,這對於程式和使用者不能合理地修正錯誤時很有意義,但同時程式可能執行不完整。

   比如,下面的程式碼使用CDate轉換一個字串為日期格式,如果失敗了,錯誤處理代理程式就分配給start_date變數的值為當前的日期:   

Private Sub ValidateStartDate(ByVal date_string As String) Dim start_date As Date ' 安裝錯誤處理代理 On Error GoTo InvalidDate ' 轉換字串為日期格式 start_date = CDate(date_string) ' 執行關於這個日期的一些操作 : ' 不進入下面的錯誤處理代理程式中 Exit Sub InvalidDate: ' 如果日期,就使用當前日期 start_date = Date Resume Next End Sub


   請注意,儘量不要使用這種方法處理錯誤,因為它採取了“安靜”的方式,而不是明顯地告訴使用者發生了錯誤。應該是這樣,當使用者輸入了非法日期後,就提示他發生了錯誤,並要求輸入一個新的數值。

   Exit Sub/Function/Property

   如果程式不能繼續它的任務,可以立即使用 Exit Sub、 Exit Function 或者 Exit Property 實現退出。下面的程式碼是上面那段程式的一個新版本,如果日期字串非法,就告訴給使用者,然後退出程式:

  

Private Sub ValidateStartDate(ByVal date_string As String) Dim start_date As Date ' 安裝錯誤處理代理 On Error GoTo InvalidDate ' 轉換字串為日期 start_date = CDate(date_string) ' 執行關於這個日期的一些操作 : ' 不進入下面的錯誤處理代理程式中 Exit Sub InvalidDate: ' 如果日期非法,就告訴使用者,然後退出 MsgBox "The start date """ & _ date_string & _ """ is invalid. Please enter a new one." Exit Sub End Sub


   按照這種方式退出程式,的程式就不可能告訴使用者錯誤發生了。這意味著:不管程式是否成功,呼叫程式能夠合適地繼續執行時,才適於採用這個方法。如果呼叫者必須要知道錯誤發生了,那麼程式碼中就必須要使用Err.Raise命令來簡短地進行一下描述。

  End Sub、 End Function End Property

   如果錯誤處理代理程式最後接觸到程式的 End Sub、 End Function 或者 End Property 語句,就會產生自然執行Exit命令的效果。比如,上面那段程式碼的最後部分可以這麼編寫:

  

InvalidDate: ' 如果日期非法,就告訴使用者,然後退出 MsgBox "The start date """ & _ date_string & _ """ is invalid. Please enter a new one." End Sub


   可是有時候,這麼處理會帶來一些混淆。因為,也可能要處理新的錯誤,那麼再隨後新增新的錯誤處理代理程式時,就有可能注意不到上一個錯誤處理代理程式沒有使用Exit語句做為結尾,這樣,就會發生上一個錯誤處理代理程式直接執行到下一個程式碼段中。比如,下面的程式碼段中,如果遇到一個非法日期,將會顯示2段出錯提示資訊,而不是實際應該存在的1個:

InvalidDate: ' 如果日期非法,就告訴使用者,然後退出 MsgBox "The start date """ & _ date_string & _ """ is invalid. Please enter a new one." ReadFileError: '讀取檔案錯誤 MsgBox "Error reading the data." : End Sub


   因此,為了避免這種錯誤的發生,不要讓錯誤處理代理程式直接接觸到過程的End命令做為結束。要使用Exit命令完成離開程式的功能!

   Err.Raise

   Err物件提供了一個Raise方法,它允許程式產生一個新錯誤,或者再次產生上一次的錯誤。它的語法如下:

Err.Raise Number, [], [Description], [Helpfile], [Helpcontext]


   Number

錯誤程式碼。為了在類模組中建立一個新的錯誤程式碼,將 vbError 加上實際的程式碼。
比如: vbObjectError + 1001。

   Source

產生錯誤的物件或者應用的名字。對於物件,使用Project.Class格式。對於程式,使用Project.Routine格式。比如,MyProgram.LoadData.

  
   Description
關於錯誤的描述字串。

   Helpfile
關於錯誤詳細資訊的幫助檔名字。error.

   Helpcontext
幫助檔案中對應這個錯誤的上下文ID。

  錯誤資訊處理策略

   如果程式本身不能處理錯誤,就應該生成一個新的錯誤,其中帶有相關環境的資訊。比如,下面的程式檢視讀取一個資料檔案,如果沒有找到檔案,FileOpenError錯誤處理代理程式就生成了myappErrNoInputFile錯誤,這將提供給呼叫程式比Visual Basic原本錯誤資訊更多的資料。Visual Basic對這個錯誤的資訊就是“檔案沒有發現”,然而經過處理後的新出錯資訊是“輸入資料檔案沒有發現”。甚至於,Err.Description域可以包含沒有發現的檔名。

' 定義應用程式錯誤常量 Private Const myappErrNoInputFile = vbObjectError + 1000 : '定義Visual Basic錯誤常量 Private Const vbErrFileNotFound = 53 : Private Sub ReadInputData(ByVal file_name As String) Dim file_number As Integer '開啟檔案 file_number = FreeFile On Error GoTo FileOpenError Open file_name For Input As file_number ' 操作檔案出錯 On Error GoTo FileReadError : ' 操作檔案 : '關閉檔案 Close file_number Exit Sub FileOpenError: ' 開啟檔案錯誤 If Err.Number = vbErrFileNotFound Then '如果檔案沒有找到,轉換錯誤資訊為myappErrNoInputFile. Err.Raise myappErrNoInputFile, _ "MyApp.ReadInputData", _ "Could not open input file """ & _ file_name & """." Else '其他錯誤處理 Err.Raise Err.Number, _ Err.Source, _ Err.Description, _ Err.HelpFile, _ Err.HelpContext End If Exit Sub FileReadError: '讀取檔案錯誤 : Exit Sub End Sub


   程式可以使用與下面類似的程式碼來執行。錯誤處理代理使用儲存在Err物件中的資訊,這個資訊由Raise方法產生。

On Error GoTo DataInputError ReadInputData "c:mydata.dat" Exit Sub DataInputError: ‘裝載資料錯誤 MsgBox "Error" & Str$(Err.Number) & _ " loading the input data." & vbCrLf & _ Err.Description


   如前所示,程式格式化了出錯提示資訊。為了簡單化處理,程式可以不格式化由Raise命令產生的錯誤描述。比如,

Err.Raise myappErrNoInputFile, _ "MyApp.ReadInputData", _ "Error" & Str$(myappErrNoInputFile) & _ " opening the input file."


   當錯誤發生時,撲捉到錯誤的代理程式將可能顯示類似下面的資訊:

Error -2147220504 loading the input data. Error -2147220504 opening the input file.


   將格式化資訊的任務交給實際記錄錯誤資訊並給使用者提供顯示資訊的程式。

  定義錯誤常數

定義的通常錯誤資訊程式碼範圍是從1到65,535,其中從1到1000做保留使用,從31,000到31073被Visual Basic使用。其餘範圍內的值就是你可以自定義的了。

   微軟同時建議:定義新的錯誤常數時,最好在常數vbObjectError上加上一定的數值,比如:
Private Const myclassErrNoInputFile = vbObjectError + 1000

   如果遵循了這些原則,自定義的錯誤程式碼就不會與微軟公司的相覆蓋。

   但是很不幸,這並不能保證你所定義的錯誤程式碼不與其他開發者或者用到的庫函式中定義的錯誤常數發生衝突。為了防止這個衝突,可以定義一個基值,比如vbObjectError,然後再按一定的次序定義新錯誤常數。比如:   

Public Const rayErrorBase = 45300 Public Const rayParametersNotSet = rayErrorBase + 1 Public Const rayInvalidSphereFormat = rayErrorBase + 2 Public Const rayLightAtEye = rayErrorBase + 3 :


   如果隨後發現新定義的錯誤常數與其他程式發生衝突,就可以迅速地透過改變基值實現重新定義了所有的錯誤常量。

   將錯誤處理程式單獨存放

   請使用Resume、Resume Next、Exit Sub/Function/Property、End Sub/Function/Property 或者 Err.Raise來結束錯誤處理代理程式的編寫。永遠不要發生從一個錯誤代理進入到另一個錯誤代理的程式碼處理過程。

   比如,下面的程式碼就發生了這種不好的情況:   

Private Sub LoadData(ByVal filename As String) Dim fnum As Integer ‘檔案還沒有開啟 On Error GoTo FileIsClosed ’開啟檔案 fnum = FreeFile Open filename For Input As fnum ‘檔案被開啟了 On Error GoTo FileIsOpen ’讀取資料 : 進入錯誤處理代理,關閉檔案 On Error Resume Next FileIsOpen: ‘關閉檔案 Close fnum FileIsClosed: ’執行其他任務 : ‘結束程式 End Sub


   這段程式碼就存在幾個問題。首先,它讓人感到困惑。當新增新的錯誤處理代理程式時,可能就會發生誤會。同時,程式碼中沒有顯示錯誤資訊,相反,程式碼“安靜”地執行,就好像沒有發生任何錯誤。所以,為了防止這些,一定記住,單獨地編寫每塊錯誤處理代理程式。

   理解錯誤處理程式的作用範圍

 當程式遇到錯誤時,Visual Basic檢查在當前程式段中是否安裝了錯誤處理代理程式。如果安裝了,控制將轉移到那個程式段中。如果沒有發現,Visual Basic上移到呼叫堆疊中的呼叫相應,檢查是否在那個程式段中安裝了錯誤處理代理程式。如果安裝了,將在那個錯誤代理中恢復執行。如果仍沒有發現,就一直上移,直到找到一個安裝的錯誤處理代理程式。如果最終沒有找到,程式將崩潰。

   因此,所有Visual Baisc程式碼執行時,開始要設有一個事件處理代理或者一個Main程式。這意味著,如果在每個事件處理和Main程式中放置錯誤處理代理程式,就能所有的錯誤發生。然後,不管程式在哪裡發生錯誤,控制最終都能轉移到這裡。 不要巢狀使用錯誤處理程式

   錯誤處理代理程式執行上與其他程式有些不同,在一段錯誤處理代理的程式碼中,不可能啟用另外的錯誤處理代理程式。換言之,一段錯誤處理代理程式中不能使用On Error GoTo來定義撲捉它自身錯誤的程式碼。如果其中使用了這個語句,只有當這個錯誤處理代理程式執行完成並返回到主程式碼中時,新的錯誤處理代理程式才可能發生效果。

   巢狀使用錯誤代理的事情非常容易使人困惑。在下面的程式碼中,如果Subroutine2產生一個錯誤,就不能確定是否控制將轉移到Error1還是Error2中。如果Subroutine1執行正確,控制將轉到Error1,但是當Subroutine1發生錯誤時,控制將轉移到Error2:   

On Error GoTo Error1 Subroutine1 Subroutine2 Exit Sub Error1: On Error GoTo Error2 MsgBox "Error1:" & Str$(Err.Number) & "." & vbCrLf & _ Err.Description Resume Next Error2: MsgBox "Error2:" & Str$(Err.Number) & "." & vbCrLf & _ Err.Description Resume Next


   因此,不要在一段錯誤處理代理程式中使用On Error語句,應該將所有的錯誤處理代理程式段按照順序編寫。

 


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-988255/,如需轉載,請註明出處,否則將追究法律責任。

相關文章