Есть ли в VBA эквивалент Thread.Sleep()

Есть ли эквивалент Thread.Sleep() в Access VBA?

10 ответов

Решение
Declare Sub Sleep Lib "kernel32" Alias "Sleep" _
(ByVal dwMilliseconds As Long)

Используйте следующий синтаксис для вызова функции Sleep:

Sub Sleep()
Sleep 1000 'Implements a 1 second delay
End Sub 

Другой способ без использования kernel32:

Dim started As Single: started = Timer

Do: DoEvents: Loop Until Timer - started >= 1

Все остальные методы, заставляющие Excel ждать, приводят к тому, что Excel перестает отвечать на запросы. Решение, которое заставит Excel ждать при обеспечении отзывчивого пользовательского интерфейса, заключается в вызове этого Sub Sub с количеством секунд ожидания.

    Sub Wait(seconds As Integer)
      Dim now As Long
      now = Timer()
      Do
          DoEvents
      Loop While (Timer < now + seconds)
    End Sub

Пара поправок требуется, чтобы заставить код работать. Код ниже является исправленной версией.

Declare Sub Sleep Lib "kernel32" Alias "Sleep" (ByVal dwMilliseconds As Long)

Sub SleepVBA() 
Sleep 1000 'Implements a 1 second delay 
End Sub 

Я использую это в Excel, и это прекрасно работает:

Application.Wait DateAdd("s", 1, Now())

DateAdd () - это функция, которая устанавливает время относительно Now() (в этом случае - вы можете использовать другие значения в качестве аргумента), "s" это мера времени (в данном случае секунды), а приращение равно 1. Так что здесь, вызов функции говорит приложению подождать 1 секунду.

Смотрите также для получения более подробной информации об использовании DateAdd функция.

Если вы используете Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)Вы можете получить эту ошибку в объектном модуле.

Если это так, вы можете объявить это как личное:

Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)

Кроссплатформенное решение

Принятый ответ не будет работать в 64-битном VBA. Кроме того, это не будет работать на Mac.

Спасибо за указание на совместимость с Mac в вашем комментарии!

Для достижения полной совместимости с Mac требуется импортировать зависящую от платформы библиотечную функцию для приостановки выполнения потока, т. е. на Mac и в Windows. Поскольку принимает аргумент в микросекундах и использует миллисекунды, необходимо объявить обычай, который имеет дело с преобразованием.

Импорт библиотечных функций и объявлениеSubможно сделать, как показано ниже.

Это решение возникло в результате совместной работы меня и user8488913 над адаптацией его исходного решения , чтобы избежать целочисленного переполнения и обеспечить время, превышающее&HFFFFFFFFмикросекунды (~71 минута) на Mac:

      #If Mac Then
    #If VBA7 Then
        Private Declare PtrSafe Sub USleep Lib "/usr/lib/libc.dylib" Alias "usleep" (ByVal dwMicroseconds As Long)
    #Else
        Private Declare Sub USleep Lib "/usr/lib/libc.dylib" Alias "usleep" (ByVal dwMicroseconds As Long)
    #End If
#Else 'Windows
    #If VBA7 Then
        Public Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
    #Else
        Public Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
    #End If
#End If
#If Mac Then
'Sub providing a Sleep API consistent with Windows on Mac (argument in ms) 
'Authors: Cristian Buse,      https://stackoverflow.com/a/71176040/12287457
'         Guido Witt-Dörring, https://stackoverflow.com/a/74262120/12287457
Public Sub Sleep(ByVal dwMilliseconds As Long)
    Do While dwMilliseconds And &H80000000
        USleep &HFFFFFED8
        If dwMilliseconds < (&H418937 Or &H80000000) Then
            dwMilliseconds = &H7FBE76C9 + (dwMilliseconds - &H80000000) 
        Else
            dwMilliseconds = dwMilliseconds - &H418937
        End If
    Loop
    Do While dwMilliseconds > &H418937
        USleep &HFFFFFED8: dwMilliseconds = dwMilliseconds - &H418937
    Loop
    If dwMilliseconds > &H20C49B Then
        USleep (dwMilliseconds * 500& Or &H80000000) * 2& 
    Else
        USleep dwMilliseconds * 1000&
    End If
End Sub
#End If

Теперь будет доступно как для Windows, так и для Mac, а также в 32- и 64-разрядных средах.

Это можно назвать так:

      Sub ExampleSleepCall()
    Sleep 1000 'Suspends thread execution for 1 second
    'Calling application will freeze completely for that amount of time!
    'If this is undesired, look here: https://stackoverflow.com/a/74387976/12287457
End Sub

Обратите внимание, что:

  1. Хотя он принимает аргумент в миллисекундах, его разрешение на самом деле не равно 1 миллисекунде .
  2. Если вы хотите использовать внутри модуля класса и не хотите объявлять подпрограмму в дополнительном стандартном модуле, вы можете использовать приведенный выше код, но вы должны заменить все вхождения наPrivate, потому что невозможно импортировать функции API какPublicвнутри модуля класса.
  3. На Mac максимальная продолжительность ограниченаMAX_UINT = &HFFFFFFFFмикросекунд или около 4294,97 секунды (~71 минута). Вот почему пользовательский сабвуфер для Mac будет вызыватьusleepнесколько раз для входных значенийdwMilliseconds > &H418937, чтобы избежать проблем с целочисленным переполнением.
  4. В Windows (и на Mac с пользовательским сабвуфером) максимальная продолжительность ограниченаMAX_UINTмиллисекунды, около 4294967,3 секунды. (~ 49,71 дня)
  5. И в Windows, и в Mac вызов Sleep со значениями в миллисекундах, превышающимиMAX_LONG = &H7FFFFFFF(= 2147483647) требует передачи отрицательного значения VBALongзначения с максимальной продолжительностью сна, достигаемой при вызове следующим образом:Sleep -1"="Sleep &HFFFFFFFF)

Обратите внимание, что этот способ приостановки выполнения имеет серьезные недостатки в VBA!

Самое главное, что все приложения Microsoft Office выполняют код VBA в том же потоке, что и основной пользовательский интерфейс. Это значит, вызываяSleepпо существу зависает все приложение (оно будет отображаться как «не отвечающее» в диспетчере задач!). Невозможно выйти из этого состояния, не дожидаясь истечения времени или принудительно закрывая Приложение и перезапуская его. Отличается Application.Waitстрадает той же проблемой. Пока приложение не будет отображаться какnot respondingв диспетчере задач в этом случае он будет так же не реагировать на пользователя.

Способ обойти эту проблему - позвонитьDoEventsв цикле, как уже указывали другие люди. Однако это связано с другой проблемой. Поскольку приложение попытается выполнить код VBA как можно быстрее, DoEvents вызывается с максимально достижимой скоростью, по существу полностью загружая ЦП в этом единственном потоке, что приводит к высокому, ненужному использованию ЦП и энергии и потенциально замедляет другие более важные задачи в пользовательский интерфейс.

Чтобы узнать, как лучше всего приостановить выполнение VBA, посмотрите этот ответ .

Можно использовать процедуру Excel Wait() из Access VBA.

Первым шагом является обеспечение ссылки на библиотеку Excel из вашего проекта.

Когда это будет сделано, следующий код будет работать в течение десяти секунд:

Call Excel.Application.Wait(Time:=DateAdd("s",10,Now()))

Добавление

Declare Sub Sleep Lib "kernel32" Alias "Sleep" (ByVal dwMilliseconds As Long)

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

Function WaitTime(n As Double)
'Function that wait an amount of time n in seconds
TWait = Time
TWait = DateAdd("s", n, TWait)
Do Until TNow >= TWait
     TNow = Time
Loop
End Function

надеюсь это поможет:)

Если код выполняется в форме, вы можете использовать встроенный таймер формы.

Запустите таймер формы с помощью

      TimerInterval = 1000 ' Delay in ms

Объявите обработчик события:

      Private Sub Form_Timer()
    TimerInterval = 0 ' Stop the timer if don't want to repeat the event each second

    ' Execute your code here
End Sub
Другие вопросы по тегам