Две основные цели для обработки ошибок:
- Перехват ошибок, которые вы можете предсказать, но не можете контролировать пользователя (например, сохранение файла на флэш-накопителе после извлечения флэш-накопителей)
- При непредвиденных ошибках предоставьте пользователю форму, сообщающую ему, в чем проблема.Таким образом, они могут передать вам это сообщение, и вы сможете обойти их, пока вы работаете над исправлением.
Итак, как бы вы это сделали?
Прежде всего, создайте форму ошибки, которая будет отображаться при возникновении непредвиденной ошибки.
Это может выглядеть примерно так (FYI: Mine называется frmErrors):
Обратите внимание наследующие метки:
- lblHeadline
- lblSource
- lblProblem
- lblResponse
Кроме того, стандартные кнопки управления:
- Игнорировать
- Повторить
- Отмена
В этом коде нет ничего впечатляющего:
Option Explicit
Private Sub cmdCancel_Click()
Me.Tag = CMD_CANCEL
Me.Hide
End Sub
Private Sub cmdIgnore_Click()
Me.Tag = CMD_IGNORE
Me.Hide
End Sub
Private Sub cmdRetry_Click()
Me.Tag = CMD_RETRY
Me.Hide
End Sub
Private Sub UserForm_Initialize()
Me.lblErrorTitle.Caption = "Custom Error Title Caption String"
End Sub
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
'Prevent user from closing with the Close box in the title bar.
If CloseMode <> 1 Then
cmdCancel_Click
End If
End Sub
Как правило, вы хотите знать, какую кнопку нажимал пользователь при закрытии формы.
Затем создайте модуль обработчика ошибок, который будет использоваться в вашем приложении VBA:
'****************************************************************
' MODULE: ErrorHandler
'
' PURPOSE: A VBA Error Handling routine to handle
' any unexpected errors
'
' Date: Name: Description:
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'03/22/2010 Ray Initial Creation
'****************************************************************
Option Explicit
Global Const CMD_RETRY = 0
Global Const CMD_IGNORE = 1
Global Const CMD_CANCEL = 2
Global Const CMD_CONTINUE = 3
Type ErrorType
iErrNum As Long
sHeadline As String
sProblemMsg As String
sResponseMsg As String
sErrorSource As String
sErrorDescription As String
iBtnCap(3) As Integer
iBitmap As Integer
End Type
Global gEStruc As ErrorType
Sub EmptyErrStruc_S(utEStruc As ErrorType)
Dim i As Integer
utEStruc.iErrNum = 0
utEStruc.sHeadline = ""
utEStruc.sProblemMsg = ""
utEStruc.sResponseMsg = ""
utEStruc.sErrorSource = ""
For i = 0 To 2
utEStruc.iBtnCap(i) = -1
Next
utEStruc.iBitmap = 1
End Sub
Function FillErrorStruct_F(EStruc As ErrorType) As Boolean
'Must save error text before starting new error handler
'in case we need it later
EStruc.sProblemMsg = Error(EStruc.iErrNum)
On Error GoTo vbDefaultFill
EStruc.sHeadline = "Error " & Format$(EStruc.iErrNum)
EStruc.sProblemMsg = EStruc.sErrorDescription
EStruc.sErrorSource = EStruc.sErrorSource
EStruc.sResponseMsg = "Contact the Company and tell them you received Error # " & Str$(EStruc.iErrNum) & ". You should write down the program function you were using, the record you were working with, and what you were doing."
Select Case EStruc.iErrNum
'Case Error number here
'not sure what numeric errors user will ecounter, but can be implemented here
'e.g.
'EStruc.sHeadline = "Error 3265"
'EStruc.sResponseMsg = "Contact tech support. Tell them what you were doing in the program."
Case Else
EStruc.sHeadline = "Error " & Format$(EStruc.iErrNum) & ": " & EStruc.sErrorDescription
EStruc.sProblemMsg = EStruc.sErrorDescription
End Select
GoTo FillStrucEnd
vbDefaultFill:
'Error Not on file
EStruc.sHeadline = "Error " & Format$(EStruc.iErrNum) & ": Contact Tech Support"
EStruc.sResponseMsg = "Contact the Company and tell them you received Error # " & Str$(EStruc.iErrNum)
FillStrucEnd:
Exit Function
End Function
Function iErrorHandler_F(utEStruc As ErrorType) As Integer
Static sCaption(3) As String
Dim i As Integer
Dim iMCursor As Integer
Beep
'Setup static array
If Len(sCaption(0)) < 1 Then
sCaption(CMD_IGNORE) = "&Ignore"
sCaption(CMD_RETRY) = "&Retry"
sCaption(CMD_CANCEL) = "&Cancel"
sCaption(CMD_CONTINUE) = "Continue"
End If
Load frmErrors
'Did caller pass error info? If not fill struc with the needed info
If Len(utEStruc.sHeadline) < 1 Then
i = FillErrorStruct_F(utEStruc)
End If
frmErrors!lblHeadline.Caption = utEStruc.sHeadline
frmErrors!lblProblem.Caption = utEStruc.sProblemMsg
frmErrors!lblSource.Caption = utEStruc.sErrorSource
frmErrors!lblResponse.Caption = utEStruc.sResponseMsg
frmErrors.Show
iErrorHandler_F = frmErrors.Tag ' Save user response
Unload frmErrors ' Unload and release form
EmptyErrStruc_S utEStruc ' Release memory
End Function
Youмогут быть ошибки, которые будут настраиваться только для вашего приложения.Обычно это короткий список ошибок, относящихся только к вашему приложению.Если у вас еще нет модуля констант, создайте его, который будет содержать ENUM ваших пользовательских ошибок.(ПРИМЕЧАНИЕ. Office '97 НЕ поддерживает ENUMS.).ENUM должен выглядеть примерно так:
Public Enum CustomErrorName
MaskedFilterNotSupported
InvalidMonthNumber
End Enum
Создайте модуль, который будет выдавать ваши пользовательские ошибки.
'********************************************************************************************************************************
' MODULE: CustomErrorList
'
' PURPOSE: For trapping custom errors applicable to this application
'
'INSTRUCTIONS: To use this module to create your own custom error:
' 1. Add the Name of the Error to the CustomErrorName Enum
' 2. Add a Case Statement to the raiseCustomError Sub
' 3. Call the raiseCustomError Sub in the routine you may see the custom error
' 4. Make sure the routine you call the raiseCustomError has error handling in it
'
'
' Date: Name: Description:
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'03/26/2010 Ray Initial Creation
'********************************************************************************************************************************
Option Explicit
Const MICROSOFT_OFFSET = 512 'Microsoft reserves error values between vbObjectError and vbObjectError + 512
'************************************************************************************************
' FUNCTION: raiseCustomError
'
' PURPOSE: Raises a custom error based on the information passed
'
'PARAMETERS: customError - An integer of type CustomErrorName Enum that defines the custom error
' errorSource - The place the error came from
'
' Returns: The ASCII vaule that should be used for the Keypress
'
' Date: Name: Description:
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'03/26/2010 Ray Initial Creation
'************************************************************************************************
Public Sub raiseCustomError(customError As Integer, Optional errorSource As String = "")
Dim errorLong As Long
Dim errorDescription As String
errorLong = vbObjectError + MICROSOFT_OFFSET + customError
Select Case customError
Case CustomErrorName.MaskedFilterNotSupported
errorDescription = "The mask filter passed is not supported"
Case CustomErrorName.InvalidMonthNumber
errorDescription = "Invalid Month Number Passed"
Case Else
errorDescription = "The custom error raised is unknown."
End Select
Err.Raise errorLong, errorSource, errorDescription
End Sub
Теперь вы хорошо подготовлены для перехвата ошибок в вашей программе.Вы, субподрядчик (или функция), должны выглядеть примерно так:
Public Sub MySub(monthNumber as Integer)
On Error GoTo eh
Dim sheetWorkSheet As Worksheet
'Run Some code here
'************************************************
'* OPTIONAL BLOCK 1: Look for a specific error
'************************************************
'Temporarily Turn off Error Handling so that you can check for specific error
On Error Resume Next
'Do some code where you might expect an error. Example below:
Const ERR_SHEET_NOT_FOUND = 9 'This error number is actually subscript out of range, but for this example means the worksheet was not found
Set sheetWorkSheet = Sheets("January")
'Now see if the expected error exists
If Err.Number = ERR_SHEET_NOT_FOUND Then
MsgBox "Hey! The January worksheet is missing. You need to recreate it."
Exit Sub
ElseIf Err.Number <> 0 Then
'Uh oh...there was an error we did not expect so just run basic error handling
GoTo eh
End If
'Finished with predictable errors, turn basic error handling back on:
On Error GoTo eh
'**********************************************************************************
'* End of OPTIONAL BLOCK 1
'**********************************************************************************
'**********************************************************************************
'* OPTIONAL BLOCK 2: Raise (a.k.a. "Throw") a Custom Error if applicable
'**********************************************************************************
If not (monthNumber >=1 and monthnumber <=12) then
raiseCustomError CustomErrorName.InvalidMonthNumber, "My Sub"
end if
'**********************************************************************************
'* End of OPTIONAL BLOCK 2
'**********************************************************************************
'Rest of code in your sub
goto sub_exit
eh:
gEStruc.iErrNum = Err.Number
gEStruc.sErrorDescription = Err.Description
gEStruc.sErrorSource = Err.Source
m_rc = iErrorHandler_F(gEStruc)
If m_rc = CMD_RETRY Then
Resume
End If
sub_exit:
'Any final processing you want to do.
'Be careful with what you put here because if it errors out, the error rolls up. This can be difficult to debug; especially if calling routine has no error handling.
Exit Sub 'I was told a long time ago (10+ years) that exit sub was better than end sub...I can't tell you why, so you may not want to put in this line of code. It's habit I can't break :P
End Sub
Копирование / вставка приведенного выше кода может не сработать сразу же, но определенно даст вам суть.
Кстати, если вам когда-нибудь понадобится, чтобы я сделал логотип вашей компании, посмотрите на http://www.MySuperCrappyLogoLabels99.com