Automatically generating handling of issues

前端 未结 5 573
旧时难觅i
旧时难觅i 2020-11-27 18:53

This is more an observation than a real question: MS-Access (and VBA in general) is desperately missing a tool where error handling code can be generated automatically, and

相关标签:
5条回答
  • 2020-11-27 19:29

    You can always roll your own tool like Chip Pearson did. VBA can actually access it's own IDE via the Microsoft Visual Basic for Applications Extensibility 5.3 Library. I've written a few class modules that make it easier to work with myself. They can be found on Code Review SE.

    I use it to insert On Error GoTo ErrHandler statements and the appropriate labels and constants related to my error handling schema. I also use it to sync up the constants with the actual procedure names (if the function names should happen to change).

    0 讨论(0)
  • 2020-11-27 19:33

    What about using "Erl", it will display the last label before the error (e.g., 10, 20, or 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
    
    0 讨论(0)
  • 2020-11-27 19:37

    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}"
    

    This standard error code can be then automatically added to all of your procs and function by clicking on the corresponding button in the MZ-Tools menu. You'll notice that we refer here to a hidden and undocumented function in the VBA standard library, 'Erl', which stands for 'error line'. You got it! If you ask MZ-Tools to automatically number your lines of code, 'Erl' will then give you the number of the line where the error occured. You will have a complete description of the error in your immediate window, such as:

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

    Of course, once you realize the interest of the system, you can think of a more sophisticated error handler, that will not only display the data in the debug window but will also:

    1. display it as a message on the screen
    2. Automatically insert a line in an error log file with the description of the error or
    3. if you are working with Access or if you are connected to a database, automatically add a record to a Tbl_Error table!

    meaning that each error generated at the user level can be stored either in a file or a table, somewhere on the machine or the network. Are we talking about building an automated error reporting system working with VBA?

    0 讨论(0)
  • Well there are a couple of tools that will do what you ask MZ Tools and FMS Inc come to mind.

    Basically they involve adding an:

    On Error GoTo ErrorHandler
    

    to the top of each proc and at the end they put an:

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

    label with usually a call to a global error handler where you can display and log custom error messages

    0 讨论(0)
  • 2020-11-27 19:56

    There is no need to buy tools DJ mentioned. Here is my code for free:

    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
    

    Put it in a module and call it from Immediate Window every time you add new function or sub to a form or module like this (Form1 is name of your form):

    MyModule.InsertErrHandling "Form_Form1"
    

    It will alter your ode in Form1 from this:

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

    to this:

    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
    

    Create now in a module a Sub which will display the error dialog and where you can add inserting the error to a text file or database:

    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
    

    This code does not enter error handling if there is already "On Error" statement in a proc.

    0 讨论(0)
提交回复
热议问题