Есть ли в 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 миллисекунде .
- Если вы хотите использовать внутри модуля класса и не хотите объявлять подпрограмму в дополнительном стандартном модуле, вы можете использовать приведенный выше код, но вы должны заменить все вхождения на
Private
, потому что невозможно импортировать функции API какPublic
внутри модуля класса. - На Mac максимальная продолжительность ограничена
MAX_UINT = &HFFFFFFFF
микросекунд или около 4294,97 секунды (~71 минута). Вот почему пользовательский сабвуфер для Mac будет вызыватьusleep
несколько раз для входных значенийdwMilliseconds > &H418937
, чтобы избежать проблем с целочисленным переполнением. - В Windows (и на Mac с пользовательским сабвуфером) максимальная продолжительность ограничена
MAX_UINT
миллисекунды, около 4294967,3 секунды. (~ 49,71 дня) - И в 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