excel - 为什么从 Evaluate 调用时 VBA Find 循环失败?

标签 excel vba

当使用 Application.Evaluate 或 ActiveSheet.Evaluate 方法调用例程时,我在子例程内运行查找循环时遇到一些问题。例如,在下面的代码中,我定义了一个子例程 FindSub(),它在工作表中搜索字符串“xxx”。例程 CallSub() 使用标准 Call 语句和 Evaluate 调用 FindSub() 例程。

当我运行 Call FindSub 时,一切都会按预期工作:每个匹配的地址都会打印到直接窗口中,并且当代码完成时我们会收到一条最终消息“Finished up”。但是,当我执行 Application.Evaluate "FindSub()"时,仅打印出第一个匹配的地址,并且我们永远不会到达“完成”消息。换句话说,当循环尝试评估是否应该继续时,在 Cells.FindNext 行之后遇到错误,并且程序执行停止而不会打印任何运行时错误。

在这种情况下,我希望 Call FindSub 和 Application.Evaluate "FindSub()"都能产生相同的结果。有人可以解释为什么他们不这样做,如果可能的话,有办法解决这个问题吗?谢谢。

注意:在这个例子中我显然不需要使用Evaluate。此版本经过简化,只关注我在更复杂的情况下遇到的特定问题。

Sub CallSub()
    Call FindSub
    Application.Evaluate "FindSub()"
End Sub

Sub FindSub()
    Dim rngFoundCell As Range
    Dim rngFirstCell As Range

    Set rngFoundCell = Cells.Find(What:="xxx", after:=ActiveCell, LookIn:=xlValues, _
        LookAt:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
        MatchCase:=False, SearchFormat:=False)

    If Not rngFoundCell Is Nothing Then
        Set rngFirstCell = rngFoundCell
        Do
            Debug.Print rngFoundCell.Address
            Set rngFoundCell = Cells.FindNext(after:=rngFoundCell)
        Loop Until (rngFoundCell Is Nothing) Or (rngFoundCell.Address = rngFirstCell.Address)
    End If

    Debug.Print "Finished up"
End Sub

最佳答案

原因很可能是 Evaluate 将您的函数视为 UDF - 就好像它是从工作表公式中调用的一样。 UDF 对它们可以执行的操作有严格的限制 - 特别是不能设置属性或调用其他函数 - 我想这里的某些内容已经违反了这些限制,尽管我无法准确地隔离这里所做的事情。

在 UDF 内,错误会被悄悄吞掉,因为工作表公式不允许引发 VB 错误。 (如果公式错误不断引发 VB 对话框,则会破坏 Excel 用户界面)

参见http://support.microsoft.com/kb/170787了解 UDF 限制的详细信息。

编辑:好的,这是对您的问题的一些澄清,我知道您的代码在评估过程中在哪里默默地出错。使用此代码:

Sub FindSub()
    Dim rngFoundCell As Range
    Dim rngFirstCell As Range

    Set rngFoundCell = Cells.Find(What:="xxx", after:=ActiveCell, LookIn:=xlValues, _
        LookAt:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
        MatchCase:=False, SearchFormat:=False)

    If Not rngFoundCell Is Nothing Then
        Set rngFirstCell = rngFoundCell
        Do
            Debug.Print "FOUND: " & rngFoundCell.Address
            Set rngFoundCell = Cells.FindNext(after:=rngFoundCell)
            Debug.Print "FIND NEXT: " & IIf(rngFoundCell Is Nothing, " NOTHING", " SOMETHING")
        Loop Until (rngFoundCell Is Nothing) Or (rngFoundCell.Address = rngFirstCell.Address)
        Debug.Print "ESCAPED LOOP"
    End If

    Debug.Print "Finished up"
End Sub

我在立即窗口中得到以下输出:

findsub
FOUND: $G$6
FIND NEXT:  SOMETHING
FOUND: $D$11
FIND NEXT:  SOMETHING
ESCAPED LOOP
Finished up

太好了。但是:

callsub
FOUND: $G$6
FIND NEXT:  SOMETHING
FOUND: $D$11
FIND NEXT:  SOMETHING
ESCAPED LOOP
Finished up
FOUND: $G$6
FIND NEXT:  NOTHING

这里有三件事值得注意,至少当我运行它时。

  1. 该函数被调用两次。这是 Evaluate 的一个已知问题,与 Excel 如何处理工作表上的计算有关。这就是为什么 Evaluate 永远不能用在记录数据的函数上 - 因为它可以在单个 Evaluate 中被多次调用。
  2. 在第二次循环中,“查找下一个”无法找到另一个单元格。这是一个谜,但 Evaluate 不应该真正用于运行在工作表中运行的函数,因此在某种程度上,这是未定义的行为,不能真正被视为错误。评估意味着运行一个公式,其中所有单元格引用都在公式中明确映射。我自己的理论是“查找下一个”不起作用,因为您尝试使用不是事件单元格的单元格引用,而“评估”则试图消除此类非法事件。
  3. 你的错误。在 Loop Until 行中,您处理 Or 测试。麻烦的是,如果 rngFoundCellNothing,第二个测试将抛出错误; VBA 正在尝试处理完整表达式,在这种情况下无法计算 rngFoundCell.Address。当作为 UDF 运行时(即在 Evaluate 中),代码将立即退出,不会出现错误对话框。这就是为什么您在评估中看不到“已完成”的原因。

关于excel - 为什么从 Evaluate 调用时 VBA Find 循环失败?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2668090/

相关文章:

EXCEL 2010 条件格式问题

unit-testing - 如何在 VBA Excel 宏中设置单元测试?

vba - Workbooks.Open 编译错误

VBA 错误 "Bubble Up"

excel - 如何通过 VBA 代码终止任务管理器进程?

vba - 根据用户输入应用多个过滤器 (VBA)

Excel VBA - 在检测到单元格值变化时调用函数的最佳实践

vba - 如果可能的话,如何在 Excel 的 VBA 中将任何单元格转换为整数?

excel - OLEObjects OptionButtons 事件处理

excel - Excel 2003 VBA 中的简单变量赋值