On Error Goto在EventHandler subs中不起作用

时间:2015-06-19 09:53:43

标签: vba error-handling

让我们假设这段代码:

模块1:

$(document).ready(function (){
       $("#matricola").keyup(function (e) { 
         var thevalue = $(this).val();
         $.post('getMatricolaAjax.php',{'type':'user','matricola':thevalue}, 
           function(data) { 
            console.log("OK");
            $("#matricola").val("");
            alert("Matricola "+ data +" già presente!!");
           });
         });
      });

的Class1:

<?php
require_once 'config.php';
require_once FUNCTION_PATH.'/dbFunction.php';
$conn = dbConnect($USERDB, $PASSWORDDB, $NAMEDB);

if($conn->real_escape_string($_POST['type']) == "user"){
    $queryMatricolaMatch = 'select * from user where matricola = "'.$conn->real_escape_string($_POST['matricola']).'"';
}else{
    $queryMatricolaMatch = 'select * from 150ore where matricola = "'.$conn->real_escape_string($_POST['matricola']).'"';
}

$matricola = dbQueryGetResult($queryMatricolaMatch);
dbDisconnect($conn);

echo $matricola;

等级2:

Sub main()

    Dim cl As New Class2
    On Error GoTo errorhandler1
        cl.DoWork
     On Error GoTo 0
Exit Sub

errorhandler1:
    MsgBox (Err.Description)

End Sub

我希望启动errorhandler1并显示带有err.Description的MsgBox。 但它反而引发了我的运行时错误。

我需要做些什么来处理EventHandlers例程中的错误?

2 个答案:

答案 0 :(得分:3)

这只是咬了我 - 你在这个简单的C#代码中看到了:

try
{
    SomeEvent?.Invoke(this, EventArgs.Empty);
}
catch
{
    // break here
}

如果SomeEvent的任何处理程序抛出异常,AFAIK会在catch中出现断点 - 而且我期待VBA也这样做...它没有。

通过打破事件处理程序,然后检查调用堆栈,您可以看到某些在事件源与RaiseEvent调用之间滑动,以及事件处理程序过程:

"Non-Basic Code" between the event source and the event handler

我认为[<Non-Basic code>]这里将是VBA运行时本身,将事件分派给正在侦听该特定事件源实例上的事件的任何对象:这个“中间人”很可能为什么运行时错误没有冒泡:运行时可能会保护自己并在此处抛出错误,无论父堆栈帧是否有On Error语句。

您可以在该屏幕截图中看到我的解决方法的一些提示 - 添加一个新的类模块,将其命名为ErrorInfo,并为其提供一些有用的成员:

Option Explicit
Private Type TErrorInfo
    Number As Long
    Description As String
    Source As String
End Type
Private this As TErrorInfo

Public Property Get Number() As Long
    Number = this.Number
End Property

Public Property Get Description() As String
    Description = this.Description
End Property

Public Property Get Source() As String
    Source = this.Source
End Property

Public Property Get HasError() As Boolean
    HasError = this.Number <> 0
End Property

Public Property Get Self() As ErrorInfo
    Set Self = Me
End Property

Public Sub SetErrInfo(ByVal e As ErrObject)
    With e
        this.Number = .Number
        this.Description = .Description
        this.Source = .Source
    End With
End Sub

现在无论何时定义事件,都要为其添加一个参数:

Public Event Something(ByVal e As ErrorInfo)

引发该事件时,提供实例,处理错误,检查ErrorInfo对象,相应地调用Err.Raise,然后你可以处理 em>错误通常,在事件调用范围中,您希望处理事件处理程序错误:

Public Sub DoSomething()
    On Error GoTo CleanFail
    With New ErrorInfo
        RaiseEvent Something(.Self)
        If .HasError Then Err.Raise .Number, .Source, .Description
    End With
    Exit Sub
CleanFail:
    MsgBox Err.Description, vbExclamation
End sub

事件处理程序代码只需处理其错误(否则处理程序中的任何运行时错误基本上都是未处理的),并在ErrInfo参数中设置错误状态:

Private Sub foo_Something(ByVal e As ErrorInfo)
    On Error GoTo CleanFail
    Err.Raise 5
    Exit Sub
CleanFail:
    e.SetErrInfo Err
End Sub

和bingo,现在你可以干净地处理在事件源中的事件处理程序中引发的错误,而不涉及全局变量或losing the actual error information(在我的情况下,在某些第三方API中引发的错误)无用(但在大多数情况下可以说是“足够好”)“哎呀,没有用”信息。

重要警告

Cancel事件的情况一样,如果一个事件有多个处理程序,那么哪个状态返回到事件调用站点是,未定义 - 如果只有一个处理程序抛出一个错误,并且非投掷处理程序不会篡改ErrorInfo参数,那么理论上调用站点会出现一个错误。当两个或多个处理程序抛出错误时,“乐趣”就开始了。

在这种情况下,处理程序需要在修改ErrorInfo之前验证ErrorInfo的状态。

或者,另一个解决方案可能是让Property Get类封装错误信息的数组,并可能将索引器添加到ErrorInfo成员 - 或者其他任何机制可以考虑,“汇总错误”。哎呀,您甚至可以在AggregateErrorInfo集合类中封装{em>集合public async Task CreatePictureFolderAsync() { try { var folder = await DownloadsFolder.CreateFolderAsync("Pictures"); // add your folder to StorageApplicationPermissions.FutureAccessList StorageApplicationPermissions.FutureAccessList.AddOrReplace(token, folder); } catch (Exception ex) { ex.Exception("CreatePictureFolderAsync"); } } 个实例,并使您的“多个侦听器事件”在其签名中使用它。

大多数时候你只需要一个处理程序,所以这不是一个问题。

答案 1 :(得分:2)

我们可以阅读here

  

如果您使用Err对象的Raise方法引发错误,那么   可以强制Visual Basic通过调用列表向后搜索   启用的错误处理程序。

但在这种情况下,没有启用错误处理程序。

也许您可以通知class2的客户端工作失败。这里因为class2的客户端是一个标准模块,你不能使用class2中的事件,所以也许只是一个简单的只读属性可能会有帮助吗?

  

模块:

Sub main()
    cl.DoWork
    If Not cl.IsWorkOk Then MsgBox "Work failed..."
    On Error GoTo 0
    Exit Sub

errorhandler1:
    MsgBox (Err.Description)

End Sub
  

等级2:

Private m_isWorkOk As Boolean

Private Sub cl_MyEvent()
    On Error GoTo ErrMyEvent
    Call Err.Raise(123, , "ErrorInClass")
    m_isWorkOk = True
    Exit Sub
ErrMyEvent:
    m_isWorkOk = False
End Sub

Public Property Get IsWorkOk() As Boolean
    IsWorkOk = m_isWorkOk
End Property