MS-Access, VBA и обработка ошибок

Это скорее наблюдение, чем реальный вопрос: MS-Access (и VBA в целом) отчаянно не хватает инструмента, где код обработки ошибок может генерироваться автоматически, и где номер строки может отображаться при возникновении ошибки. Вы нашли решение? Что это? Я только что понял, сколько сотен часов я потратил с тех пор, как несколько лет назад нашел правильный ответ на эту основную проблему, и я хотел бы узнать, каковы ваши идеи и решения по этому очень важному вопросу.

10.12.2008 22:24:51
Вы не хотите использовать номера строк в вашем коде. Читать это: Вы не хотите, чтобы номера строк
Vlado 10.02.2019 13:20:08
Хорошо отметил и согласился. Я не хочу номера строк. И в моем коде разработки нет номеров строк. Затем, когда речь заходит о версии моих приложений, в которой я хочу регистрировать ошибки, сгенерированные пользователями, я добавляю их (автоматически), что значительно упрощает отслеживание ошибок: (1) по крайней мере Я могу проверить, действительно ли ошибки, исходящие из определенного модуля, идентичны, и (2) отладка кода происходит быстрее. И, конечно же, у меня нет процедуры с более чем 65 000 строк!
Philippe Grondier 25.03.2020 06:35:37
5 ОТВЕТОВ

Ну есть несколько инструментов , которые будут делать то , что вы спросите MZ Инструменты и FMS Inc приходят на ум.

В основном они включают добавление:

On Error GoTo ErrorHandler

в начало каждого процесса и в конце они помещают:

ErrorHandler:
  Call MyErrorhandler Err.Number, Err.Description, Err.LineNumber

метка обычно с вызовом глобального обработчика ошибок, где вы можете отображать и регистрировать пользовательские сообщения об ошибках

5
10.12.2008 22:40:02
Вы вынули слова из моих уст!
Philippe Grondier 10.12.2008 23:00:09
This is slightly misleading as Err.LineNumber doesn't exist.. So while good practice for generic error handling, it doesn't answer the crux of the original issue about line numbering. If you need to do this then the answer involving Erl would be better if you had to have a line number.
FinancialRadDeveloper 5.11.2010 12:07:33
РЕШЕНИЕ

My solution is the following:

  1. install MZ-Tools, a very interesting add-on for VBA. No they did not pay me to write this. Version 3 was free, but since version 8.0, the add-in is commercially sold.
  2. program a standard error handler code such as this one (see MZ-Tools menu/Options/Error handler):

On Error GoTo {PROCEDURE_NAME}_Error
{PROCEDURE_BODY}
On Error GoTo 0
Exit {PROCEDURE_TYPE}

{PROCEDURE_NAME}_Error:
debug.print "#" & Err.Number, Err.description, "l#" & erl, "{PROCEDURE_NAME}", "{MODULE_NAME}"

Этот стандартный код ошибки может быть автоматически добавлен ко всем вашим процессам и функциям, нажав соответствующую кнопку в меню MZ-Tools. Вы заметите, что мы ссылаемся здесь на скрытую и недокументированную функцию в стандартной библиотеке VBA, «Erl», что означает «строка ошибки». Ты понял! Если вы попросите MZ-Tools автоматически нумеровать ваши строки кода, «Erl» выдаст вам номер строки, где произошла ошибка. В вашем ближайшем окне вы получите полное описание ошибки, например:

#91, Object variable or With block variable not set, l# 30, addNewField, Utilities

Конечно, как только вы осознаете интерес системы, вы можете подумать о более сложном обработчике ошибок, который будет не только отображать данные в окне отладки, но и:

  1. отобразить это как сообщение на экране
  2. Автоматически вставить строку в файл журнала ошибок с описанием ошибки или
  3. если вы работаете с Access или если вы подключены к базе данных, автоматически добавьте запись в таблицу Tbl_Error!

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

6
21.02.2019 16:26:43
Хороший пост, но я критически отношусь к практике, когда ваш обработчик ошибок и процедура выхода не имеют одинакового имени, например, errHandler и exitRoutine. Из-за области действия меток нет причин делать их специфичными для конкретной подпрограммы. Делает вырезание и вставку чертовски намного проще.
David-W-Fenton 13.12.2008 00:25:40
Вы правы: нет необходимости иметь конкретное имя для процедуры ошибки. Но это на самом деле не имеет значения, так как вы не будете копировать / вставлять из одного процесса в другой, а просто использовать кнопку «вставить код ошибки», которая генерирует необходимые строки в соответствии с предопределенным форматом.
Philippe Grondier 13.12.2008 09:55:21
On Error Goto 0 - ненужная строка, поскольку вы выходите из процедуры в следующей строке. оператор On Error Goto ErrorHandler не применяется вне процедуры
Nick 19.04.2011 11:57:55

Как насчет использования «Erl», он будет отображать последний ярлык перед ошибкой (например, 10, 20 или 30)?

Private Sub mySUB()
On Error GoTo Err_mySUB
10:
    Dim stDocName As String
    Dim stLinkCriteria As String
20:
    stDocName = "MyDoc"
30:
    DoCmd.openform stDocName, acFormDS, , stLinkCriteria    
Exit_mySUB:
    Exit Sub
Err_mySUB:
    MsgBox Err.Number & ": " & Err.Description & " (" & Erl & ")"
    Resume Exit_mySUB
End Sub
7
10.12.2008 23:05:53

Вы всегда можете свернуть свой собственный инструмент, как это сделал Чип Пирсон . На самом деле VBA может получить доступ к своей собственной среде IDE через библиотеку Microsoft Visual Basic for Applications Extensibility 5.3 . Я написал несколько модулей классов, которые облегчают работу с самим собой. Их можно найти на Code Review SE .

Я использую его для вставки On Error GoTo ErrHandlerоператоров и соответствующих меток и констант, связанных с моей схемой обработки ошибок. Я также использую его для синхронизации констант с реальными именами процедур (если имена функций должны измениться).

2
8.05.2018 21:48:18

Там нет необходимости покупать инструменты DJ упомянутых. Вот мой код бесплатно:

Public Sub InsertErrHandling(modName As String)
    Dim Component As Object
    Dim Name As String
    Dim Kind As Long
    Dim FirstLine As Long
    Dim ProcLinesCount As Long
    Dim Declaration As String
    Dim ProcedureType As String
    Dim Index As Long, i As Long
    Dim LastLine As Long
    Dim StartLines As Collection, LastLines As Collection, ProcNames As Collection, ProcedureTypes As Collection
    Dim gotoErr As Boolean

    Kind = 0
    Set StartLines = New Collection
    Set LastLines = New Collection
    Set ProcNames = New Collection
    Set ProcedureTypes = New Collection

    Set Component = Application.VBE.ActiveVBProject.VBComponents(modName)
        With Component.CodeModule

            ' Remove empty lines on the end of the code
            For i = .CountOfLines To 1 Step -1
                If Component.CodeModule.Lines(i, 1) = "" Then
                  Component.CodeModule.DeleteLines i, 1
                Else
                    Exit For
                End If
            Next i

            Index = .CountOfDeclarationLines + 1
            Do While Index < .CountOfLines
                gotoErr = False
                Name = .ProcOfLine(Index, Kind)
                FirstLine = .ProcBodyLine(Name, Kind)
                ProcLinesCount = .ProcCountLines(Name, Kind)
                Declaration = Trim(.Lines(FirstLine, 1))
                LastLine = FirstLine + ProcLinesCount - 2
                If InStr(1, Declaration, "Function ", vbBinaryCompare) > 0 Then
                    ProcedureType = "Function"
                Else
                    ProcedureType = "Sub"
                End If
                Debug.Print Component.Name & "." & Name, "First: " & FirstLine, "Lines:" & ProcLinesCount, "Last: " & LastLine, Declaration
                Debug.Print "Declaration: " & Component.CodeModule.Lines(FirstLine, 1), FirstLine
                Debug.Print "Closing Proc: " & Component.CodeModule.Lines(LastLine, 1), LastLine

                ' do not insert error handling if there is one already:
                For i = FirstLine To LastLine Step 1
                    If Component.CodeModule.Lines(i, 1) Like "*On Error*" Then
                        gotoErr = True
                        Exit For
                    End If
                Next i
                If Not gotoErr Then
                    StartLines.Add FirstLine
                    LastLines.Add LastLine
                    ProcNames.Add Name
                    ProcedureTypes.Add ProcedureType
                End If

                Index = FirstLine + ProcLinesCount + 1
            Loop

            For i = LastLines.Count To 1 Step -1
                If Not (Component.CodeModule.Lines(StartLines.Item(i) + 1, 1) Like "*On Error GoTo *") Then
                    Component.CodeModule.InsertLines LastLines.Item(i), "ExitProc_:"
                    Component.CodeModule.InsertLines LastLines.Item(i) + 1, "    Exit " & ProcedureTypes.Item(i)
                    Component.CodeModule.InsertLines LastLines.Item(i) + 2, "ErrHandler_:"
                    Component.CodeModule.InsertLines LastLines.Item(i) + 3, "    Call LogError(Err, Me.Name, """ & ProcNames.Item(i) & """)"
                    Component.CodeModule.InsertLines LastLines.Item(i) + 4, "    Resume ExitProc_"
                    Component.CodeModule.InsertLines LastLines.Item(i) + 5, "    Resume ' use for debugging"

                    Component.CodeModule.InsertLines StartLines.Item(i) + 1, "    On Error GoTo ErrHandler_"
                End If
            Next i
        End With
End Sub

Поместите его в модуль и вызывайте его из Immediate Window каждый раз, когда вы добавляете новую функцию или подчиненную форму в форму или модуль, подобный этому (Form1 - это имя вашей формы):

MyModule.InsertErrHandling "Form_Form1"

Это изменит вашу оду в Form1 из этого:

Private Function CloseIt()
    DoCmd.Close acForm, Me.Name
End Function

к этому:

Private Function CloseIt()
    On Error GoTo ErrHandler_
        DoCmd.Close acForm, Me.Name
ExitProc_:
Exit Function
ErrHandler_:
    Call LogError(Err, Me.Name, "CloseIt")
    Resume ExitProc_
    Resume ' use for debugging
End Function

Создайте сейчас в модуле Sub, который будет отображать диалоговое окно с ошибкой и куда вы можете добавить вставку ошибки в текстовый файл или базу данных:

Public Sub LogError(ByVal objError As ErrObject, moduleName As String, Optional procName As String = "")
    On Error GoTo ErrHandler_
    Dim sql As String
    MsgBox "Error " & Err.Number & " Module " & moduleName & Switch(procName <> "", " in " & procName) & vbCrLf & " (" & Err.Description & ") ", vbCritical
Exit_:
    Exit Sub
ErrHandler_:
    MsgBox "Error in LogError procedure " & Err.Number & ", " & Err.Description
    Resume Exit_
    Resume ' use for debugging
End Sub

Этот код не входит в обработку ошибок, если в процедуре уже есть оператор «On Error».

2
10.02.2019 18:13:19