Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / Microsoft Access Новый топик    Ответить
 Обработка ошибок  [new]
Lekks
Member

Откуда:
Сообщений: 147
Уважаемые знатоки! ))) Посмотрите, пожалуйста код, на предмет необходимости обработки ошибок, поскольку я с данным вопросом что-то пока не разобрался. Не пойму, где нужна, а где можно без этого обойтись. Не буду против конструктивной критики самого кода))))

+
Option Compare Database

Option Explicit

Private Sub BirthDate_GotFocus()
Me.BirthDate.SelStart = 0
End Sub

Private Sub CancCmd_Click()
    Me.Undo
    Me.Position.Value = Null
    Me.Rank.Value = Null
End Sub

Private Sub CloCmd_Click()
    Me.Undo
    Me.Position.Value = Null
    Me.Rank.Value = Null
    DoCmd.Close acForm, "FnewPeopleRst"
End Sub

Private Sub Form_BeforeUpdate(Cancel As Integer)
  Dim rstPR As ADODB.Recordset
 On Error GoTo ExitHere
    If IsNull(Me.FName) Or IsNull(Me.LName) Or IsNull(Me.PName) Then
        MsgBox "Не заполнены обязательные сведения (Фамилия/Имя/Отчество)", vbOKOnly + vbCritical, "НЕДОСТАТОЧНО ДАННЫХ"
        Cancel = True
    ElseIf DCount("*", "People", "(People.FName & People.LName & People.PName)='" & (Me.FName & Me.LName & Me.PName) & "'") > 0 Then
        MsgBox "Данный человек имеется в базе", vbOKOnly + vbCritical, "ДУБЛИКАТ ДАННЫХ"
        Me.Undo
    ElseIf PeopleSt.Value = 0 And (IsNull(Me.Position) Or IsNull(Me.Rank)) Then
        MsgBox "Не заполнены сведения о сотруднике (Должность/Звание)", vbOKOnly + vbCritical, "НЕДОСТАТОЧНО ДАННЫХ"
        Cancel = True
    ElseIf vbNo = MsgBox("Вы хотите сохранить новые данные?", vbYesNo + vbQuestion, "ОБНАРУЖЕН НОВЫЙ НАРУШИТЕЛЬ/СОТРУДНИК") Then
            Me.Undo
            Me.Position.Value = Null
            Me.Rank.Value = Null
    Else
      If PeopleSt.Value = 0 Then
        Set rstPR = New ADODB.Recordset
         With rstPR
            .Open "Officers", CurrentProject.Connection, adOpenDynamic, adLockPessimistic
            .AddNew
            .Fields("PeID") = Me.PeopleID
            .Fields("Position") = Me.Position
            .Fields("Rank") = Me.Rank
            .Update
         End With
        rstPR.Close
        Set rstPR = Nothing
        Me.Position.Value = Null
        Me.Rank.Value = Null
       Else: DoCmd.GoToRecord , , acNewRec
       End If
    End If
ExitHere:
    Exit Sub
End Sub

Private Sub Form_Load()
    PeopleSt.Value = -1
    DoCmd.MoveSize Height:=2500
    DoCmd.GoToRecord , , acNewRec
    Me.Position.ColumnCount = 2
    Me.Position.ColumnWidths = "0;50"
    Me.Position.RowSource = "SELECT * FROM Positions ORDER BY PositID"
    Me.Rank.ColumnCount = 2
    Me.Rank.ColumnWidths = "0;50"
    Me.Rank.RowSource = "SELECT * FROM Ranks ORDER BY RankID"
End Sub

Private Sub PeopleSt_Click()
    If PeopleSt.Value = 0 Then
            DoCmd.MoveSize Height:=5800
            Me.BirthDate.Visible = False
        Else
            DoCmd.MoveSize Height:=2500
            Me.BirthDate.Visible = True
        End If
End Sub

Private Sub SaveCmd_Click()
    On Error GoTo ExitHere
     DoCmd.GoToRecord , , acNewRec
ExitHere:
    Exit Sub
End Sub
7 ноя 18, 21:06    [21727401]     Ответить | Цитировать Сообщить модератору
 Re: Обработка ошибок  [new]
Joss
Member

Откуда: г. Минск
Сообщений: 4844
Поставь себе надстройку MZTools и там обработчик ошибок ставится нажатием одной кнопки. Ставь везде и не парься рассуждениями надо-не надо. Раз затрудняешься определить, то ставь.
8 ноя 18, 09:15    [21727700]     Ответить | Цитировать Сообщить модератору
 Re: Обработка ошибок  [new]
MrShin
Member

Откуда:
Сообщений: 1239
Так у вас практически никакой обработки ошибок и нет - пользователь не увидит никакого сообщения об ошибке и не сможет ничего сообщить разработчику, он даже скорее всего не поймет, что что-то не работает.
Обработчики нужны во всех процедурах, которые вызываются системой (события), иначе при ошибке возникнет необработанная ошибка и пользователя может выкинуть в код, это недопустимо. Поцедуры, которые вызываются из кода могут и не иметь обработчика, ошибка будет передана в вызывающую процедуру, но при этом код и текст ошибки не будут соответствовать той линии кода, где ошибка действительно возникла, остановка будет на вызове процедуры без обработчика и сообщение об ошибки будет весьма невнятным.

Еще момент - обработчик ошибок должен проводить финальные операции типа закрытия рекордсетов или разрушения переменных даже при возникновении ошибок, иначе это может привести к нестабильности системы.

Я использую два типа обработчиков, каждый имеет несколько модификаций, но это не принципиально. Первый тип - для событий и кода, который непосредственно взаимодествует с пользователем

Private Sub cmdConfirm_Click()
    Dim rst As DAO.Recordset
    On Error GoTo ErrorHandler
    
    DoCmd.Hourglass True
    Set rst = dbLocal.OpenRecordset("qryReport")
    With rst
        While Not .EOF
            
            'делаем что-то
            Call MyFunction
            .MoveNext
        Wend
    End With
ExitHere:
    On Error Resume Next
    ' курсор будет нормальным и рекордсет закрыт даже в случае ошибки
    DoCmd.Hourglass False
    rst.Close
    Set rst = Nothing

    Exit Sub
    'следующая команда никогда не выполняется автоматически, но нужна для удобства отладки:
    'когда появляется сообщение об ошибке, нажимаем Ctrl-Break, переходим в отладчик, Ctrl-Shift-F8 для возврата из LogError
    'затем ставим курсор на Resume, Ctrl-F9 для перемещения указателя выполнения на Resume, и по F8 попадаем точно на инструкцию с ошибкой. Можно исправить и повторить попытку заново без перезапуска процедуры
    Resume '>> remove in release
ErrorHandler:
    'эта функция записывает ошибку в базу и выводит сообщение для пользователя
    LogError Err.Number, Err.Description, Erl, "cmdConfirm_Click", "Form_frm_InputDialog"
    Resume ExitHere
End Sub


Второй тип - для процедур, вызываемых из кода:
Public Function MyFunction()
10        On Error GoTo ErrorHandler
          ' делаем что-то
ExitHere:
20        Exit Function
30        Resume '>> remove in release
ErrorHandler:
40        Debug.Assert Not (STOP_AT_ERROR And IS_DEV) '>> remove in release
50        Err.Raise Err.Number, "MyFunction of Form_frm_InputDialog", Err.Description & vbCrLf & "in MyFunction of Form_frm_InputDialog at " & Erl
End Function

Здесь задача обработчика - добавить к описанию ошибки информацию о процедуре и линии, где возникла ошибка, новое описание передается в вызвавшую процедуру. В результате, если в вызвавшей процедуре будет обработчик первого типа, то появится сообщение со теком вызова процедур, что поможет определить причину гораздо проще.

Debug.Assert в обработчике второго типа нужен для отладки - если обе булевские глобальные константы STOP_AT_ERROR и IS_DEV (определены в стандартном модуле) установлены в True, то код остановится в этом месте в случае возникновения ошибки без возврата в вызывающую процедуру, что позволит очень легко найти строку с ошибкой с помощью Resume, проанализировать переменные, при необходимости исправить код и повторить выполнение без перезапуска процедуры.

Есть модификация обработчика второго типа с финальной секцией для закрытия курсоров и т.п.:
Public Function MyFunction()
    On Error GoTo ErrorHandler
    ' делаем что-то

ExitHere:
    On Error Resume Next
    'сюда пишем финализирующие команды, которые должны выполняться всегда
    Dim err_num As Long, err_descr As String, err_ln As String
    If Len(err_descr) > 0 Then GoTo ErrorRaise
    Exit Function
    Resume '>> remove in release
ErrorHandler:
    err_num = Err.Number: err_descr = Err.Description: err_ln = Erl
    Debug.Assert Not (STOP_AT_ERROR And IS_DEV) '>> remove in release
    Resume ExitHere
ErrorRaise: On Error GoTo 0
    Err.Raise err_num, "MyFunction of Form_frm_InputDialog", err_descr & vbCrLf & "in MyFunction of Form_frm_InputDialog at " & err_ln
End Function

Я также всегда нумерую строки кода, чтобы в сообщении об ошибке выводился номер строки, где возникла ошибка.
Для генерации обработчиков и нумерации использую MZ-Tools, очень удобный инструмент, не представляю без него нормальной работы. Обработчики и нумерация есть и в бесплатной 3-й версии, но 8-я намного удобнее, я ее даже купил, хотя можно постоянно сбрасывать пробный месячный период.

Текст LogError:
+

Option Compare Database
Option Explicit

'// module with error handling functions
    ' Purpose: Generic error handler.
    ' Logs errors to table "ErrorLog".
    ' Arguments: lngErrNumber - value of Err.Number
    ' strErrDescription - value of err.description
    ' strLine - code line number (Erl) Erl=0 if no row number in the line
    ' strCallingProc - name of sub|function that generated the error.
    ' strCallingModule - name of code module that generated the error.
    ' vParameters - optional string: List of parameters to record.
    ' bShowUser - optional boolean: If False, suppresses display.
Function LogError(ByVal lngErrNumber As Long, ByVal strErrDescription As String, strLine As String, _
                  strCallingProc As String, Optional strCallingModule As String, Optional vParameters = "{Missing}", Optional bShowUser As Boolean = True) As Boolean
    On Error GoTo Err_LogError
    Dim strMsg As String                              ' String for display in MsgBox
    Dim rst    As DAO.Recordset                       ' The Aph_tblErrorLog table
    Select Case lngErrNumber
        Case 0
            Debug.Print strCallingProc & " called error 0."
        Case 2501                                     ' Cancelled
            'Do nothing.
'        Case 3314, 2101, 2115                         ' Can't save.
'            If bShowUser Then
'                strMsg = "Record cannot be saved at this time." & vbCrLf & "Complete the entry, or press <Esc> to undo."
'                MsgBox strMsg, vbExclamation, "Error"
'            End If
        Case Else
            If bShowUser Then
                strMsg = "Error " & lngErrNumber & ": " & strErrDescription & vbCrLf & "in " & _
                strCallingProc & " of " & strCallingModule & " at " & strLine
                Interaction.MsgBox strMsg, vbExclamation, "Error " & Now()
            End If
            'log the error to database
            Set rst = CurrentDb.OpenRecordset("tbl_BMS_ErrorLog", , dbAppendOnly)
            rst.AddNew
            rst![ErrorNum] = lngErrNumber
            rst![ErrorDescription] = Left$(strErrDescription, 255)
            rst![ErrLine] = strLine
            rst![CallingProc] = strCallingProc
            rst![Module] = strCallingModule
            rst![CreatedTimeStamp] = Now()
            rst![UserName] = GetCurrentUserID()
            If Not IsMissing(vParameters) Then
                rst![Parameters] = Left(vParameters, 255)
            End If
            rst.Update
            rst.Close
            LogError = True
    End Select

Exit_LogError:
    Set rst = Nothing
    Exit Function
Err_LogError:
    strMsg = "An unexpected situation arose in your program." & vbCrLf & _
             "Please write down the following details:" & vbCrLf & vbCrLf & _
             "Calling Proc: " & strCallingProc & vbCrLf & _
             "Error Number " & lngErrNumber & " in line " & strLine & vbCrLf & strErrDescription & vbCrLf & vbCrLf & _
             "Unable to record because Error " & Err.Number & " in line " & Erl & vbCrLf & Err.Description
    Interaction.MsgBox strMsg, vbCritical, "LogError()"
    Resume Exit_LogError
End Function

8 ноя 18, 10:01    [21727750]     Ответить | Цитировать Сообщить модератору
 Re: Обработка ошибок  [new]
MrShin
Member

Откуда:
Сообщений: 1239
Забыл добавить номера строк в 2 примера. Они должны быть в продакшне и тесте.
8 ноя 18, 10:03    [21727760]     Ответить | Цитировать Сообщить модератору
 Re: Обработка ошибок  [new]
Lekks
Member

Откуда:
Сообщений: 147
MrShin
Так у вас практически никакой обработки ошибок и нет


Lekks
... поскольку я с данным вопросом что-то пока не разобрался...
))))

Joss
Ставь везде и не парься рассуждениями надо-не надо.


Так и поступлю))) Пойду MZTools качать))) Спасибо!
8 ноя 18, 17:00    [21728423]     Ответить | Цитировать Сообщить модератору
Все форумы / Microsoft Access Ответить