【问题标题】:vba error handling in loop循环中的vba错误处理
【发布时间】:2011-11-30 23:36:12
【问题描述】:

vba 新手,尝试“on error goto”,但我不断收到“index out of range”错误。

我只想制作一个组合框,由包含查询表的工作表的名称填充。

    For Each oSheet In ActiveWorkbook.Sheets
        On Error GoTo NextSheet:
         Set qry = oSheet.ListObjects(1).QueryTable
         oCmbBox.AddItem oSheet.Name

NextSheet:
    Next oSheet

我不确定问题是否与将 On Error GoTo 嵌套在循环内有关,或者如何避免使用循环。

【问题讨论】:

    标签: vba error-handling


    【解决方案1】:

    问题可能是您还没有从第一个错误中恢复。您不能从错误处理程序中抛出错误。您应该添加一个 resume 语句,如下所示,这样 VBA 就不再认为您在错误处理程序中:

    For Each oSheet In ActiveWorkbook.Sheets
        On Error GoTo NextSheet:
         Set qry = oSheet.ListObjects(1).QueryTable
         oCmbBox.AddItem oSheet.Name
    NextSheet:
        Resume NextSheet2
    NextSheet2:
    Next oSheet
    

    【讨论】:

    • 错误:无错误恢复
    • 简历对我来说是个好建议!对我来说重要的是要理解,由 On Error Goto 引用的行标签被视为错误处理例程。并且这些例程必须用 Resume、Exit sub、exit function 或 exit propoerty 关闭。
    【解决方案2】:

    作为在您的示例代码之类的循环中处理错误的一般方法,我宁愿使用:

    on error resume next
    for each...
        'do something that might raise an error, then
        if err.number <> 0 then
             ...
        end if
     next ....
    

    【讨论】:

      【解决方案3】:

      怎么样:

          For Each oSheet In ActiveWorkbook.Sheets
              If oSheet.ListObjects.Count > 0 Then
                oCmbBox.AddItem oSheet.Name
              End If
          Next oSheet
      

      【讨论】:

      • 难道没有不是查询表的“列表对象”吗?我需要工作表有一个查询表。
      • @Justin,如果是这样,请为ListObjects(1).QueryTable Is Nothing 添加一个测试——你的代码也没有这个测试。我的示例的要点是在取消引用第一个元素之前检查 ListObjects 集合是否有任何元素。
      【解决方案4】:

      实际上,Gabin Smith 的答案需要稍作更改才能正常工作,因为您无法在没有错误的情况下继续。

      Sub MyFunc()
      ...
          For Each oSheet In ActiveWorkbook.Sheets
              On Error GoTo errHandler:
              Set qry = oSheet.ListObjects(1).QueryTable
              oCmbBox.AddItem oSheet.name
      
          ...
      NextSheet:
          Next oSheet
      
      ...
      Exit Sub
      
      errHandler:
      Resume NextSheet        
      End Sub
      

      【讨论】:

        【解决方案5】:

        还有另一种控制错误处理的方法非常适用于循环。创建一个名为here 的字符串变量,并使用该变量来确定单个错误处理程序如何处理错误。

        代码模板为:

        On error goto errhandler
        
        Dim here as String
        
        here = "in loop"
        For i = 1 to 20 
            some code
        Next i
        
        afterloop:
        here = "after loop"
        more code
        
        exitproc:    
        exit sub
        
        errhandler:
        If here = "in loop" Then 
            resume afterloop
        elseif here = "after loop" Then
            msgbox "An error has occurred" & err.desc
            resume exitproc
        End if
        

        【讨论】:

          【解决方案6】:

          我不想为我的代码中的每个循环结构制作特殊的错误处理程序,因此我有一种方法可以使用我的标准错误处理程序来查找问题循环,这样我就可以为它们编写一个特殊的错误处理程序。

          如果在循环中发生错误,我通常想知道导致错误的原因,而不是跳过它。为了找出这些错误,我像许多人一样将错误消息写入日志文件。但是,如果循环中发生错误,则写入日志文件是危险的,因为每次循环迭代都会触发错误,在我的情况下,80 000 次迭代并不少见。因此,我将一些代码放入我的错误记录函数中,以检测相同的错误并跳过将它们写入错误日志。

          用于每个过程的标准错误处理程序如下所示。它记录了错误类型、发生错误的过程以及过程接收到的任何参数(在本例中为 FileType)。

          procerr:
              Call NewErrorLog(Err.number, Err.Description, "GetOutputFileType", FileType)
              Resume exitproc
          

          我写入表的错误记录功能(我在 ms-access 中)如下。它使用静态变量来保留错误数据的先前值并将它们与当前版本进行比较。记录第一个错误,然后如果我是用户,则第二个相同的错误会将应用程序推入调试模式,或者如果处于其他用户模式,则退出应用程序。

          Public Function NewErrorLog(ErrCode As Variant, ErrDesc As Variant, Optional Source As Variant = "", Optional ErrData As Variant = Null) As Boolean
          On Error GoTo errLogError
          
              'Records errors from application code
              Dim dbs As Database
              Dim rst As Recordset
          
              Dim ErrorLogID As Long
              Dim StackInfo As String
              Dim MustQuit As Boolean
              Dim i As Long
          
              Static ErrCodeOld As Long
              Static SourceOld As String
              Static ErrDataOld As String
          
              'Detects errors that occur in loops and records only the first two.
              If Nz(ErrCode, 0) = ErrCodeOld And Nz(Source, "") = SourceOld And Nz(ErrData, "") = ErrDataOld Then
                  NewErrorLog = True
                  MsgBox "Error has occured in a loop: " & Nz(ErrCode, 0) & Space(1) & Nz(ErrDesc, "") & ": " & Nz(Source, "") & "[" & Nz(ErrData, "") & "]", vbExclamation, Appname
                  If Not gDeveloping Then  'Allow debugging
                      Stop
                      Exit Function
                  Else
                      ErrDesc = "[loop]" & Nz(ErrDesc, "")  'Flag this error as coming from a loop
                      MsgBox "Error has been logged, now Quiting", vbInformation, Appname
                      MustQuit = True  'will Quit after error has been logged
                  End If
              Else
                  'Save current values to static variables
                  ErrCodeOld = Nz(ErrCode, 0)
                  SourceOld = Nz(Source, "")
                  ErrDataOld = Nz(ErrData, "")
              End If
          
              'From FMS tools pushstack/popstack - tells me the names of the calling procedures
              For i = 1 To UBound(mCallStack)
                  If Len(mCallStack(i)) > 0 Then StackInfo = StackInfo & "\" & mCallStack(i)
              Next
          
              'Open error table
              Set dbs = CurrentDb()
              Set rst = dbs.OpenRecordset("tbl_ErrLog", dbOpenTable)
          
              'Write the error to the error table
              With rst
                  .AddNew
                  !ErrSource = Source
                  !ErrTime = Now()
                  !ErrCode = ErrCode
                  !ErrDesc = ErrDesc
                  !ErrData = ErrData
                  !StackTrace = StackInfo
                  .Update
                  .BookMark = .LastModified
                  ErrorLogID = !ErrLogID
              End With
          
          
              rst.Close: Set rst = Nothing
              dbs.Close: Set dbs = Nothing
              DoCmd.Hourglass False
              DoCmd.Echo True
              DoEvents
              If MustQuit = True Then DoCmd.Quit
          
          exitLogError:
              Exit Function
          
          errLogError:
              MsgBox "An error occured whilst logging the details of another error " & vbNewLine & _
              "Send details to Developer: " & Err.number & ", " & Err.Description, vbCritical, "Please e-mail this message to developer"
              Resume exitLogError
          
          End Function
          

          请注意,错误记录器必须是应用程序中最可靠的功能,因为应用程序无法正常处理错误记录器中的错误。出于这个原因,我使用 NZ() 来确保 null 不会潜入。请注意,我还将 [loop] 添加到第二个相同的错误中,以便我知道首先查看错误过程中的循环。

          【讨论】:

            【解决方案7】:

            怎么样?

            If oSheet.QueryTables.Count > 0 Then
              oCmbBox.AddItem oSheet.Name
            End If 
            

            或者

            If oSheet.ListObjects.Count > 0 Then
                '// Source type 3 = xlSrcQuery
                If oSheet.ListObjects(1).SourceType = 3 Then
                     oCmbBox.AddItem oSheet.Name
                End IF
            End IF
            

            【讨论】:

              猜你喜欢
              • 2020-03-09
              • 2021-04-12
              • 1970-01-01
              • 2017-03-22
              • 1970-01-01
              • 2015-02-07
              • 1970-01-01
              • 2013-05-18
              • 1970-01-01
              相关资源
              最近更新 更多