Почему происходит сбой цикла VBA Find при вызове из Evaluate? - PullRequest
3 голосов
/ 19 апреля 2010

У меня возникают некоторые проблемы при запуске цикла поиска внутри подпрограммы, когда процедура вызывается с использованием метода Application.Evaluate или ActiveSheet.Evaluate. Например, в приведенном ниже коде я определяю подпрограмму FindSub (), которая ищет на листе строку «xxx». Подпрограмма CallSub () вызывает подпрограмму FindSub (), используя как стандартную инструкцию Call, так и Evaluate.

Когда я запускаю Call FindSub, все будет работать так, как ожидается: каждый соответствующий адрес распечатывается в непосредственном окне, и мы получаем окончательное сообщение «Закончено», когда код готов. Однако когда я выполняю Application.Evaluate «FindSub ()», распечатывается только адрес первого совпадения, и мы никогда не достигаем сообщения «Закончено». Другими словами, после строки Cells.FindNext возникает ошибка, поскольку цикл пытается определить, следует ли продолжать его, и выполнение программы останавливается без печати ошибки времени выполнения.

Я бы ожидал, что оба вызова 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

Ответы [ 2 ]

1 голос
/ 19 апреля 2010

Причина, скорее всего, в том, что Evaluate видит вашу функцию как UDF - как если бы она была вызвана из формулы рабочего листа. UDF имеют серьезные ограничения на то, что они могут делать - в частности, не устанавливать свойства или вызывать другие функции - и я предполагаю, что что-то здесь нарушило эти ограничения, хотя я не могу точно выделить, что здесь сделано.

Внутри UDF ошибки проглатываются молча, потому что формуле листа не разрешено генерировать ошибки VB. (Это нарушило бы пользовательский интерфейс Excel, если ошибка формулы постоянно вызывала диалоги VB)

Подробнее об ограничениях UDF см. http://support.microsoft.com/kb/170787.

РЕДАКТИРОВАТЬ: Хорошо, вот некоторые разъяснения по вашей проблеме, и я знаю, где ваш код молча ошибается во время оценки. Используя этот код:

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. Во втором цикле Find Next не может найти другую ячейку. Это загадка, но на самом деле Evaluate не должен использоваться для запуска функций, которые бегают по листу, поэтому, в некотором смысле, это неопределенное поведение и его нельзя считать ошибкой. Evaluate предназначен для запуска формулы, где все ссылки на ячейки отображаются в формуле явно. Моя собственная теория заключается в том, что Find Next не работает, потому что вы пытаетесь использовать ссылку на ячейку, которая не является активной ячейкой, а Evaluate пытается уничтожить такую ​​незаконную деятельность.
  3. Ваш баг. В строке Loop Until вы обрабатываете тест Or. Проблема в том, что если rngFoundCell равно Nothing, второй тест выдаст ошибку; VBA пытается обработать полное выражение, и rngFoundCell.Address не может быть вычислено в этом случае. Код будет немедленно завершен без диалогового окна с ошибкой при запуске в качестве пользовательской функции (т. Е. В Evaluate). Вот почему вы не видите "Закончено" внутри Evaluate.
0 голосов
/ 19 апреля 2010

Должно работать следующее:

Call FindSub
Call Application.Run("FindSub") 

Для меня .Evaluate не удается и ничего не делает.

Если я использую Call Application.Run("FindSub()") (с паренами), я вижу то же поведение, что и вы («частичный» второй вызов).

Вы также можете попробовать Application.Evaluate "FindSub"

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...