2010-04-19 2 views
3

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

Когда я запустил Call FindSub, все будет работать так, как ожидалось: каждый соответствующий адрес будет распечатан в ближайшее окно, и мы получим окончательное сообщение «Закончено», когда код будет выполнен. Однако, когда я делаю 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 
+0

Существует некоторая интересная дискуссия об ограничениях .Evaluate по этому вопросу: http://stackoverflow.com/questions/2611929/stop-vba-evaluate-from-calling-target-function-twice –

+0

@Richard , который также задал автор этого вопроса =) –

+0

@Joel, Ooops. Это меня не очень уважало. Спасибо :) Тем не менее, это хороший вопрос для других людей, чтобы просмотреть, если они сталкиваются с некоторыми. Оцените странность. –

ответ

1

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

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

Для получения дополнительной информации о ограничениях UDF см. http://support.microsoft.com/kb/170787.

EDIT: Хорошо, вот некоторые пояснения по вашей проблеме, и я знаю, где ваш код беззвучно вызывает ошибки во время оценки. Используя этот код:

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 

я получаю следующий результат в окне Immediate:

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 вы обрабатываете тест «Или». Проблема в том, что если rngFoundCell - Nothing, второй тест выдаст ошибку; VBA пытается обработать полное выражение и rngFoundCell.Address не может быть оценен в этом случае. Код будет немедленно выходить без диалога ошибок при запуске как UDF (т. Е. В рамках оценки). Вот почему вы не видите «Готово» внутри Evaluate.
+0

Я просто собирался это сказать, но я все еще был озадачен тем фактом, что OP оценивает Sub, Думаю, все будет работать. То же самое с «.Find». Я просто сделаю это для нечетких недокументов «Оценить» ... – jtolle

+0

Это, конечно же, совершает один UDF-грех, который должен поиграть с другими ячейками за кулисами (независимо от того, измените ли вы их или нет) , он разрушит дерево зависимостей). Мне показалось странным, что Find убежал невредимым. Наверное, этого не должно было быть. –

+0

Очень информативное сопровождение ... спасибо! – jtolle

0

должно работать:

Call FindSub 
Call Application.Run("FindSub") 

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

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

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

Смежные вопросы