【问题标题】:Automatically generating handling of issues自动生成问题处理
【发布时间】:2010-09-26 08:21:40
【问题描述】:

这与其说是一个真正的问题,不如说是一个观察结果:MS-Access(以及一般的 VBA)非常缺少一个可以自动生成错误处理代码以及在发生错误时可以显示行号的工具。你找到解决办法了吗?它是什么?我才意识到自从几年前我找到了这个基本问题的正确答案以来,我节省了多少数百小时,我想看看你对这个非常重要的问题有什么想法和解决方案。

【问题讨论】:

  • 您不想在代码中使用行号。阅读:You don't want line numbers
  • 很好,并同意。我不想要行号。而且我的开发代码中没有任何行号。然后,当涉及到我的应用程序的用户版本时,我希望能够记录用户生成的错误,我正在添加它们(以自动方式),使错误后续更容易:(1)至少我可以检查来自特定模块的错误是否确实相同,并且 (2) 调试代码更快。当然,我没有任何超过 65 000 行的程序!

标签: vba ms-access error-handling


【解决方案1】:

如果使用“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

【讨论】:

    【解决方案2】:

    我的解决方案如下:

    1. 安装MZ-Tools,一个非常有趣的VBA插件。不,他们没有付钱给我写这篇文章。第 3 版是免费的,但从 8.0 版开始,该插件开始商业销售。
    2. 编写标准错误处理程序代码,例如这个(参见 MZ-Tools 菜单/选项/错误处理程序):

    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 菜单中的相应按钮将此标准错误代码自动添加到您的所有 proc 和函数中。您会注意到,我们在这里引用了 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 吗?

    【讨论】:

    • 好帖子,但我对错误处理程序和退出例程没有统一名称的做法持批评态度,例如 errHandler 和 exitRoutine。由于标签范围,没有理由使它们特定于特定的子。使剪切和粘贴地狱变得更加容易。
    • 你是对的:不需要为错误例程指定一个特定的名称。但这并不重要,因为您不会从一个 proc 复制/粘贴到另一个,而是使用“插入错误代码”按钮,根据预定义的格式生成所需的行。
    • On Error Goto 0 是不必要的行,因为您将在下一行退出该过程。 On Error Goto ErrorHandler 语句不适用于过程之外
    【解决方案3】:

    嗯,有几个工具可以满足您对MZ ToolsFMS Inc 的要求。

    基本上它们涉及添加:

    On Error GoTo ErrorHandler
    

    到每个过程的顶部 最后他们放了一个:

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

    标签通常调用全局错误处理程序,您可以在其中显示和记录自定义错误消息

    【讨论】:

    • 这有点误导,因为 Err.LineNumber 不存在。因此,虽然通用错误处理的良好做法,但它并没有回答有关行号的原始问题的症结。如果您需要这样做,那么如果您必须有一个行号,那么涉及 Erl 的答案会更好。
    【解决方案4】:

    您始终可以像Chip Pearson 那样推出自己的工具。 VBA 实际上可以通过Microsoft Visual Basic for Applications Extensibility 5.3 Library 访问它自己的IDE。我编写了一些类模块,使自己更容易使用。他们可以在Code Review SE找到。

    我使用它来插入On Error GoTo ErrHandler 语句以及与我的错误处理架构相关的适当标签和常量。我还使用它来将常量与实际的过程名称同步(如果函数名称发生变化)。

    【讨论】:

      【解决方案5】:

      没有必要购买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
      

      把它放在一个模块中,每次你向这样的表单或模块添加新函数或子时从即时窗口调用它(Form1 是你的表单的名称):

      MyModule.InsertErrHandling "Form_Form1"
      

      它将改变您在 Form1 中的 ode:

      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
      

      如果 proc 中已经存在“On Error”语句,则此代码不会进入错误处理。

      【讨论】:

      • 如果我们的子函数或函数中有 On Error 语句(覆盖添加到开头的 On Error GoTo ErrHandler_)会很好,但有时它不会工作,因此我们的错误处理将是死代码。
      • 也许将每个 On Error GoTo 0 更改为 On Error GoTo ErrHandler_ 就可以完成这项工作,只是想知道。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多