【问题标题】:How to return a description with the form_error event in MS Access如何在 MS Access 中返回带有 form_error 事件的描述
【发布时间】:2026-01-17 01:15:01
【问题描述】:

我在 MS Access 中使用 form_error 事件来捕获错误,它只给出 dataerr integer 而不是像 vba 错误这样的描述,但是 MS Website 给出了事件的以下描述:

您可以将 DataErr 参数与 Error 函数一起使用来映射 编号对应的错误信息

但实际上并没有给出这个例子,我正在努力弄清楚。

具体情况是用户将更新子表单中的记录,某些字段可能会破坏参照完整性规则,数字只会告诉我参照完整性规则已被破坏,而描述会给我表名,这使我可以给出与发生错误的字段相关的规定性错误消息

有没有简单的方法来做到这一点?

【问题讨论】:

  • 您可以尝试DBEngine.Errors 集合(这与VBA.Err 对象不同),请参阅msdn.microsoft.com/en-us/library/office/ff835711.aspx。获取有关 ODBC 错误的更多信息很有用,我不确定它是否会对您的情况有所帮助。
  • 我试过在不同的地方触发这个,但集合总是空的,集合似乎更多地与尝试打开外部数据库有关,而不是在一个内部工作

标签: ms-access vba


【解决方案1】:

创建一个单独的函数来执行所有验证并向用户发送任何缺失的信息。在表单的 beforeupdate 事件中运行它,并在需要时取消该事件。这可能会消除您的许多 form_error。在 form_error 事件中,您可以调用相同的函数。发现问题,取消默认消息。如果需要,您可以单独处理弹出的任何其他错误。

【讨论】:

  • 这对于丢失数据等很好。不过,我特别指的是参照完整性。为了检查这一点,我相信我必须走 DLookup 路线,这似乎需要付出很多努力来规避访问内置的表关系功能。
  • 我同意这并不理想,但是,您(以及我们所有人)想要的 AFAIK 并不存在。如果您使用组合框和/或列表框进行数据输入,则查找应该最少。
  • 我认为之前的评论也是我的答案。如果用户输入可以打破参考。完整性,不允许任意输入,让用户从组合框中选择。 @HelloWorld
  • 在这种情况下,我希望用户能够直接检索和处理多条记录,这就是为什么我专门查看错误和更改事件,而不是使用标准链接到按钮/组合框错误转到更新/插入等。我同意在字段/表单上使用更新前事件似乎是最好的解决方法,我会这样投票,但如果有人有,请留下未答复直接解决方案
【解决方案2】:

通常只是将数字传递给Error来获取描述:

MsgBox Error(DataErr)

【讨论】:

  • 不幸的是,这只是在 msgbox 中返回应用程序定义或对象定义的错误
  • 你是在子函数本身运行的吗?无论如何,我相信常见的错误是:[DataErr 2169 Customization]tek-tips.com/viewthread.cfm?qid=1080271
  • 是的,它在子函数中运行。您提供的链接与上面的 AVG 建议使用更新前事件而不是错误事件时的建议相同,但同样不是指参照完整性检查,而是指数量足够的错误情况。这似乎令人沮丧,这是解决方案。我的代码中需要的错误描述就在默认消息中,但似乎没有办法使用它
【解决方案3】:

总之

据我所知@AVG 的解决方案可能是最好的解决方法,但有一个例外。也就是说,@AVG 可能正确地认为最好的解决方法是:尝试在 Form_BeforeUpdate 事件中捕获错误;并且(可能取决于您的需要)取消Form_BeforeUpdate 中的事件。

但是,最好不要抑制Form_Error 级别的默认错误。如果您在Form_BeforeUpdate 捕获并取消错误,那么Form_Error 将不会出现错误。因此,在这种情况下,无需抑制 Form_Error 处的错误。但是,如果有另一种类型的参照完整性违规正在等待中(例如,类别/组织违规而不是类别/子类别违规),您没有想过作为开发人员处理,那么在@987654330 中会出现默认错误消息@会提醒你。您不希望因未捕获的错误而隐藏消息。

下面我提供了此解决方法的示例代码。

我还讨论了AccessError(DataErr) 看起来是一个可能的解决方案,但并不完全是。也就是说,它可以用于将DataErr 数字映射到消息。这部分有帮助。但是,不幸的是,这些消息是可变的 unsubmitted 版本。因此,例如,我们不能在代码中使用AccessError(DataErr) 来询问特定引用完整性违规消息。

使问题更尖锐

当用户通过以下表单触发Form_Error 事件: 违反参照完整性(您已精确设置以防止此类尝试无效数据状态);违反了在表级别设置的验证规则;否则 (?) 会产生数据错误。

但是,返回的默认消息可能对用户不友好。例如,在违反参照完整性的情况下......

您无法添加或更改记录,因为表“CategorySubcategory”中需要相关记录。

在此示例中,表“CategorySubcategory”表示表“Category”和“CategorySubcategory”之间的多对多关系,定义了属于特定类别的特定子类别。 (在我的示例中)在 CategorySubcategory 中还表示某些类别没有子类别。

我们需要一种方法来使用对用户友好的消息来代替对用户不友好的消息。为此,可以方便地在出现默认错误消息时对其进行询问、抑制它并替换您自己的错误消息。

(据我所知)最接近Form_Error 中的默认消息是AccessError(DataErr)。与Error(DataErr) 不同,AccessError 承诺

Microsoft > Docs > Office VBA Reference > Access > Object model > Application object > Methods > AccessError

...返回与 Microsoft Access 或 DAO 错误相关的描述性字符串[强调]

[承诺是您] 可以使用 AccessError 方法从表单的错误事件中返回描述性字符串。

所以你可能会想做类似的事情,如果你能做类似的事情会很方便......

Private Sub Form_Error(DataErr As Integer, Response As Integer)

  If AccessError(DataErr) Like "*CategorySubcategory*" Then
    MsgBox "[My user friendly message] Choose the right Subcategory given the Category; or change the Category.", vbOKOnly Or vbExclamation
    Response = acDataErrContinue ' Suppress default message
  End If

End Sub

AccessError 可以工作吗?

然而,事实证明AccessError 只返回变量 unsubmitted 版本的错误消息,而不是用户在运行时看到的变量 submitted 消息。例如 AccessError 返回类似 ...

您无法添加或更改记录,因为表“|”中需要相关记录。

...而不是...

您无法添加或更改记录,因为表“CategorySubcategory”中需要相关记录。

为了证明这会触发 data 错误(例如,通过违反参照完整性),请使用以下内容。

Private Sub Form_Error(DataErr As Integer, Response As Integer)

  ' For some reason you'll need to call AccessError(DataErr) before Error(DataErr) 
  Debug.Print "AccessError(DataErr): ", AccessError(DataErr)
  Debug.Print "Error(DataErr): ", Error(DataErr)

End Sub

同时观察:AccessError 返回一条特定消息,其中 Error 返回无用的通用“应用程序定义或对象定义错误”;并且AccessError 返回变量未替换 版本的错误消息。

@Hello World 正确引用 MS Docs > ... > Form.Error event (Access)

您可以将 DataErr 参数与 Error 函数一起使用,以将数字映射到相应的错误消息

即使用AccessError 代替Error @Hello World 的哀叹仍然公平......

[MS 文档没有] 实际上给出了这个例子,我正在努力弄清楚。

将消息映射到DataErr 号码

MS 文档作者可能想到的是如下代码

Function AccessAndJetErrorsTable() As Boolean
' Source: Access 2000 Help "Determine the Error Codes Reserved by Microsoft Access
'          and the Microsoft Jet Database Engine"
    Const strTable As String = "zttblAccessAndJetErrors"
    Dim cat As New ADOX.Catalog
    Dim tbl As New ADOX.Table
    Dim cnn As ADODB.Connection
    Dim rst As New ADODB.Recordset, lngCode As Long
    Dim strAccessErr As String
    
    Const conAppObjectError = "Application-defined or object-defined error"
    On Error GoTo Error_AccessAndJetErrorsTable

    Set cnn = CurrentProject.Connection
        ' Create Errors table with ErrorNumber and ErrorDescription fields.
    tbl.Name = strTable
    tbl.Columns.Append "ErrorCode", adInteger
    tbl.Columns.Append "ErrorString", adLongVarWChar
    
    Set cat.ActiveConnection = cnn
    cat.Tables.Append tbl
    ' Open recordset on Errors table.
    rst.Open strTable, cnn, adOpenStatic, adLockOptimistic
    ' Loop through error codes.
    For lngCode = 0 To 35000
        On Error Resume Next
        ' Raise each error.
        strAccessErr = AccessError(lngCode)
        DoCmd.Hourglass True
        ' Skip error numbers without associated strings.
        If strAccessErr <> "" Then
            ' Skip codes that generate application or object-defined errors.
            If strAccessErr <> conAppObjectError Then
                ' Add each error code and string to Errors table.
                rst.AddNew
                rst!ErrorCode = lngCode
                ' Append string to memo field.
                rst!ErrorString = strAccessErr
                rst.Update
            End If
        End If
    Next lngCode
    ' Close recordset.
    rst.Close
    DoCmd.Hourglass False
    RefreshDatabaseWindow
    MsgBox strTable & " errors table created."
    AccessAndJetErrorsTable = True

Exit_AccessAndJetErrorsTable:
    Exit Function

Error_AccessAndJetErrorsTable:
    MsgBox Err & ": " & Err.Description
    AccessAndJetErrorsTable = False
    Resume Exit_AccessAndJetErrorsTable
End Function

要使用该代码,请创建一个新数据库,将代码放入模块中,将光标放在代码中间,然后按 F5(运行它)。将创建一个表 zttblAccessAndJetErrors,您可以在其中查找 Access 和 Jet 错误消息的所有变量未替换版本;并将这些与DataErr 号码相对应。

该代码将为您提供最新的错误消息列表。但是,作为替代方案,您可以只查找发布此内容的在线资源。例如。 FMS > Microsoft Access 2010 Error Numbers and Descriptions

因此,无论是使用zttblAccessAndJetErrors 还是在线查找,您现在都可以确定与您在测试期间看到的错误消息最接近的DataErr 数字。所以这让我们有能力做类似的事情......

Private Sub Form_Error(DataErr As Integer, Response As Integer)

  Select Case DataErr
    Case 3201
      ' "You cannot add or change a record because a related record is required in table '|'."
      
        MsgBox "[My user friendly message] Choose the right Subcategory given the Category; or change the Category.", vbOKOnly Or vbExclamation
        Response = acDataErrContinue ' Suppress default message

  End Select
End Sub

也就是说,我们不直接在Form_Error中使用AccessError来返回消息。相反,我们使用AccessError(或依赖其他人使用)来生成 DataErr/Message 映射。在Form_Error 中,我们继续仅匹配DataErr 号码。但是现在我们可以将相关消息复制为我们DataErr 号码的评论。

但是,至少在这种参照完整性 (DataErr 3201) 的情况下,我们的问题仍然存在。如果我们抑制特定参照完整性违规的默认消息(如类别/子类别不匹配),就像上面的代码一样,那么就有隐藏其他特定参照完整性违规的风险。例如。如果针对用户选择的类别和组织发生了另一个参照完整性违规,那么上述代码将错误地表达(以用户友好的方式)错误地表示发生了类别/子类别违规。

可能的最佳解决方法

所以,据我所知@AVG 的解决方案可能是最好的解决方法,但有一个例外。也就是说,@AVG 可能正确地认为最好的解决方法是:尝试在 Form_BeforeUpdate 事件中捕获错误;并且(可能取决于您的需要)取消Form_BeforeUpdate 中的事件。

不过,最好不要抑制Form_Error 级别的默认错误。如果您在Form_BeforeUpdate 捕获并取消错误,那么Form_Error 将不会出现错误。因此,在这种情况下,无需抑制 Form_Error 处的错误。但是,如果有另一种类型的参照完整性违规正在等待中(例如,类别/组织违规而不是类别/子类别违规),您没有想过作为开发人员处理,那么在@987654368 中会出现默认错误消息@会提醒你。您不希望因未捕获的错误而隐藏消息。

因此,就我而言,解决方法代码看起来像(必须按照@Hello World 的建议“走 DLookup 路线”)。

Private Sub Form_BeforeUpdate(Cancel As Integer)

  ' Head off a Form_Error referential integrity error 3201:
  ' "You cannot add or change a record because a related record is required in table 'CategorySubcategory'."
  Dim subCategoryIDGivenCategoryIDAtTableLevel As Variant
  If IsNull(Me.cboSubcategory) Then
    subCategoryIDGivenCategoryIDAtTableLevel = DLookup("[SubcategoryID]", "[CategorySubcategory]", "[CategoryID] = " & Me.cboCategory)

    If Not IsNull(subCategoryIDGivenCategoryIDAtTableLevel) Then
      ' The user has not chosen a value for cboSubcategory, when they should have chosen some value
      message = "For this Category you need to choose a Subcategory. Either choose a Subcategory or change the Category."
      Me.cboSubcategory.SetFocus
      Cancel = True
    End If

  End If

  If Cancel Then
    MsgBox message, vbOKOnly Or vbExclamation, GetApplicationTitle() & " (Record Level Validation)"
  End If

End if

Private Sub Form_Error(DataErr As Integer, Response As Integer)
  Select Case DataErr
    Case 3201
      ' A referential integrity error ...
      ' "You cannot add or change a record because a related record is required in table '|'."
      
      ' We display the default error message in case we haven't caught a referential integrity
      ' condition in Form_BeforeUpdate
      Response = acDataErrDisplay

  End Select
End Sub

【讨论】:

    【解决方案4】:

    我总是使用以下代码模式,以便用户可以得到正确的错误消息,然后他们可以将其传达给我以了解原因。

    Private Sub cmdSearch_Click()
    On Error GoTo Sub_Err
    
         --Your code here---
    
    'errors catching stuff
    Sub_Exit:
        Exit Sub
    Sub_Err:
        MsgBox Error$
        Resume Sub_Exit
    End Sub
    

    对您在代码中使用的每个sub 使用相同的内容。

    【讨论】:

    • 这与 form_error 事件无关,没有点击按钮,并且在这个问题的情况下 error$ 没有返回任何内容,这正是问题所在