【问题标题】:On Error GoTo not working; Code breaks错误转到不工作;代码中断
【发布时间】:2011-08-14 21:21:59
【问题描述】:

我正在编写一个 VBA 函数来将数据从一个表导入 Access 中的另一个表。我要导入的表具有更严格的数据约束(即类型、大小等),所以我预计会出现很多错误。

我希望我的记录集循环跳过整个当前记录,并在遇到错误时在单独的表中记录它,而不是筛选出现的每个 VBA 错误。所以我插入的每一行都是On Error GoTo RecordError。但由于某种原因,它并没有处理所有错误。我的代码只是中断并告诉我错误是什么。我已经选中了“中断未处理的异常”选项。

这是一个应该解释它的屏幕截图。

为什么它会在错误处理程序之后立即中断?

【问题讨论】:

  • 你能提供一个你看到的错误信息的例子吗?
  • 错误信息不相关。该错误与我的数据库字段的格式有关,如果没有设置On Error GoTo...,我希望得到一条错误消息。事实上,我的代码正在破坏而不是转到我设置的标签。
  • 我问的原因是因为Access中触发了一些使用VBA无法捕获的错误消息。
  • On Error Resume Next 解决了你所有的问题。

标签: ms-access error-handling vba


【解决方案1】:

您需要将On Error 行放在您希望处理其错误的代码之前。

更重要的是,您只需要有一个On Error 行。然后错误处理程序保持活动状态,直到子例程退出或您执行另一个 On Error 语句。

【讨论】:

  • 这是我的假设,但它似乎不起作用。我注释掉了我所有的On Error GoTo RecordError 语句,除了第一个语句,我仍然得到相同的结果。一旦我设置了On Error GoTo...,我的代码就不应该在该子例程中进一步中断。正确的??除非我将 On Error 更改为其他内容。
【解决方案2】:

我认为您不了解 VB(A) 错误处理的工作原理。遵循以下原则:

  • On Error... 语句仅适用于它出现的例程(Sub 或 Function)(尽管它也会捕获从您使用它的例程中调用的例程“冒泡”的错误)。
  • On Error 设置状态。也就是说,一旦您发出 On Error...,它在接下来的例程中仍然有效,除非被新的 On Error... 取代。
  • On Error...有四种形式:

    1. On Error GoTo <label><label> 必须在同一个例程中定义,方法是在单独一行中紧跟一个冒号 (:) 编写标签名称。
    2. On Error Resume:立即重试抛出错误的语句。几乎从未使用过,因为它可能是无限的。
    3. On Error Resume Next:忽略错误并继续。有时在清理例程结束时很有用(例如,如果您想关闭可能打开或未打开的记录集)。或者,如果您在任何可能引发错误的行之后检查Err 对象立即,也可以使用此表单(如果Err.Number 为零(0),则语句成功而不会引发错误) .在大多数情况下,这方式工作量太大了。
    4. On Error GoTo 0:关闭错误处理。

鉴于此,通常将On Error... 语句紧跟在例程的声明之后(SubFunction 语句),尽管有些人将Dim 语句放在两者之间。如果您想临时更改例程中的错误处理方式,请将“新”放在要应用的代码之前,然后(如果使用)将“恢复”(重新发布原始代码)放在后面.

即使考虑到这一切,我也不知道为什么在选择“未处理错误时中断”时它会在错误抛出行上中断,除非您设法将它弄糊涂,以至于它认为没有主动错误处理(如果是这样的话,如果它编译了,我会感到惊讶)。

请注意,David Heffernan 在他的回答中给了你这个重要的部分,它在我之前就在这里......

【讨论】:

    【解决方案3】:

    VBA 的错误处理是一个真正的 PITA。我建议你看看this answer to the 'MS-Access, VBA and error handling' question,让它适应你自己的情况。您可以轻松编写一些代码,将所有错误消息存储在一个表中,从而构建一个事实上的错误报告系统。

    【讨论】:

      【解决方案4】:

      没有人真正回答你的问题。

      假设你的代码是这样的(一个骨架框架):

      Public Sub MySub()
      On Error GoTo errHandler
        Dim rs As DAO.Recordset
      
        Set rs = CurrentDB.OpenRecords([SQL SELECT])
        If rs.RecordCount >0 Then
           rs.MoveFirst
           Do Until rs.EOF
             [do whatever that produces the error]
      errSkipToNext:
             rs.MoveNext
           Loop
        End If
      
      exitRoutine:
        If Not (rs Is Nothing) Then
           rs.Close
           Set rs = Nothing
        Exit Sub
      
      errHandler:
        Select Case Err.Number
          Case X, Y, Z ' where these are error numbers you want to ignore
            Err.Clear
            ' do whatever it is you need to do in order to record the offending row
            Call RecordError(rs!PK, Err.Number) ' PK is a field that identifies the bad record
            GoTo errSkipToNext
          Case Else
            MsgBox Err.Number & ": " & Err.Description, vbExclamation, _
               "Error!"
            Resume exitRoutine
        End Select
      End Sub
      

      在此代码中,您在错误处理程序中使用 SELECT CASE 来决定要忽略哪些错误。在上面的代码框架中,我将错误编号列为X, Y, Z,但您可以将其替换为您想要忽略的真实错误编号。

      您不想忽略每一个错误,因为您最终可能会忽略子例程中其他地方的重要错误。如果您不想弄清楚要忽略的错误数量有限,我建议您在代码块的开头设置一个标志,该标志会产生您要忽略的错误,然后使用`If​​ bolErrorInCodeBlockToIgnore Then 决定是否忽略所有错误。像这样的:

      Public Sub MySub()
      On Error GoTo errHandler
        Dim rs As DAO.Recordset
        Dim bolErrorInCodeBlockToIgnore As Boolean
      
        Set rs = CurrentDB.OpenRecords([SQL SELECT])
        If rs.RecordCount >0 Then
           rs.MoveFirst
           Do Until rs.EOF
             bolErrorInCodeBlockToIgnore = True
             [do whatever that produces the error]
      errSkipToNext:
             rs.MoveNext
           Loop
        End If
      
      exitRoutine:
        If Not (rs Is Nothing) Then
           rs.Close
           Set rs = Nothing
        Exit Sub
      
      errHandler:
        If bolErrorInCodeBlockToIgnore Then
           Err.Clear
           ' do whatever it is you need to do in order to record the offending row
           Call RecordError(rs!PK, Err.Number) ' PK is a field that identifies the bad record
           bolErrorInCodeBlockToIgnore = False
           GoTo errSkipToNext
        Else
           MsgBox Err.Number & ": " & Err.Description, vbExclamation, _
              "Error!"
           Resume exitRoutine
        End If
      End Sub
      

      我更喜欢第一个,因为我坚信只忽略已知错误,而不是任何发生的旧错误。但可能很难想出能产生所有你想忽略的可能错误的测试。

      【讨论】:

        【解决方案5】:

        它不起作用的原因是您不能在错误处理程序中使用 On Error Goto ...。

        http://www.cpearson.com/excel/errorhandling.htm

        您不能使用 On Error 跳过几行,而是在错误时应该转到错误处理程序,然后恢复到所需的下一行(在您的示例中,您可能会使用一个包含下一个恢复的错误处理程序将带您回到下一个字段)。

        感谢 Tim Williams 在这个问题上:The second of 2 'On Error goto ' statements gets ignored

        而且 ZIP 上的 BTW ParseInt 会破坏以 0 开头的邮政编码,邮政编码可能应该被视为文本。

        【讨论】:

        • 这就是答案——至少在我的情况下:我使用On Error GoTo _label_ 跳过了For 循环中的一些代码行。谢谢!
        【解决方案6】:

        我也看到错误处理失败。这是一个例子。

        Public Function Have(ByVal item As Variant) As Boolean
        'Have = Have data.  Simplifies handling nulls and empty strings in validation code
        
            On Error GoTo Procerr
        
            If IsNull(item) Then
                Have = False
            **ElseIf Len(Trim(item)) = 0 Then  'Faster than Item <> ""**
                Have = False
            ElseIf item = 0 Then
                Have = False
            Else
                Have = True
            End If
        
        exitproc:
            Exit Function
        
        Procerr:
            'Errors sometimes occur if an unbound control is referenced
            Have = False
        
        End Function
        

        代码有时会在标有 ** 的行上失败。这是错误消息。

        请注意,错误处理程序已失败。在这种情况下,调用返回代码的表单将其记录源动态设置为空记录集,因此屏幕上的字段不可见。该表单是一个连续的表单,因此当使用空记录集加载表单时,记录和字段不可见。我的代码没有直接调用 have() 函数,但不知何故似乎是由 me.requery 方法触发的。 have() 在我的代码中被调用了数亿次,但这是导致它失败的唯一实例,并且没有调用错误处理程序。

        致兰斯·罗伯茨 (Lance Roberts) 的原始问题。 utf-8 unicode 有时会对 ms-access 造成严重破坏,因为它似乎允许数据与指令代码混淆(我的猜测)。如果数据最初是从文本文件加载的,则 utf-8 可以进入您的数据。带有字节顺序标记 (BoM) 的 utf-8 特别讨厌。当您运行一些处理数据的程序时,可能会出现奇怪的错误,并且您的文件可能看起来已经损坏。在其他情况下,文本处理函数会给出错误的答案,例如Mid() 将查看 BOM,如果您指定起点将从 BOM 开始,但 Len() 将忽略 BOM。我推测如果你有这个问题,那么 ms-access 可能无法正确处理错误。我在导入数据和导入 utf-8 时遇到了类似的问题,因为 ANSI 是原因。请注意,对于纯英文数据,utf-8 和 ANSI 在大多数情况下是相同的,因此您的错误可能不会出现在每一行。我的错误主要是时间-日期字段。尝试先导出数据,然后将其强制为 ANSI 并删除任何 BoM 并重新导入。

        【讨论】:

        • 谢谢 Andoriyu,我的系统设置为在所有错误上中断,这就是问题所在。
        【解决方案7】:

        将调试模式设置为“中断所有错误”将使程序执行停止在导致错误的行,即使错误处理程序已正确编写。这可能会造成混淆,因为错误处理似乎不起作用。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2015-01-19
          • 1970-01-01
          • 1970-01-01
          • 2016-05-14
          • 2019-11-12
          • 1970-01-01
          • 2020-10-03
          相关资源
          最近更新 更多