21

I know I'm doing something wrong here. I'm trying to use the sleep function to delay my code, but I get "Sub or Function not defined" error. Any tips?

sooprise
  • 22,657
  • 67
  • 188
  • 276

7 Answers7

27

VBA does not have a Sleep function.

You can import it from Kernel32.dll like this:

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

Note that this will freeze the application.
You can also call DoEvents in a While loop, which won't freeze the application.

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • Where in the code do I put that declaration? I've tried sticking it in outside and inside of the sub I'm working on, but get errors both times. – sooprise Apr 29 '10 at 13:39
  • Oh, oops, I was trying to put it into an excel object (silly me). I put it in a module and now it works. Thanks so much! I checked your answer. – sooprise Apr 29 '10 at 13:45
  • Despite the issue of hanging the application, I would prefer this method since the loop approach uses too much CPU. – KalenGi Aug 02 '13 at 12:44
  • @kalengi: You should be using async. – SLaks Aug 04 '13 at 23:36
  • @SLaks async with Sleep, or how do you mean? – KalenGi Aug 06 '13 at 10:15
  • @kalengi: Ideally, `Await Task.Delay()`. In VBA, I don't know whether there is any proper way to do it. – SLaks Aug 06 '13 at 14:03
  • This solution does not work in 64-bit Windows applications or on Mac. `DoEvents` in a while loop will cause excessive CPU usage. Look at [this solution](https://stackoverflow.com/a/74387976/12287457), which solves all of these issues. – GWD Nov 10 '22 at 12:08
19

Everything I've tried seems to hang the application, including Application.Wait. This seems to work though:

waitTill = Now() + TimeValue("00:15:00")

While Now() < waitTill
    DoEvents
Wend
Anthony Hayward
  • 2,164
  • 21
  • 17
  • 1
    I like this one and it is working great.. only one exception. I can't find a way to do 500 ms ... So 1 second is the minimum wait time. Is there a way to do less than 1 second? – BobNoobGuy Jul 06 '15 at 17:24
  • You can use GetSystemTime in kernel32.dll if you want higher resolution: http://www.freevbcode.com/ShowCode.asp?ID=1618 Or if in Excel, you can call the spreadsheet now() function (as [now()] from VBA) which also has higher resolution: https://groups.google.com/forum/#!topic/microsoft.public.excel.programming/-D6lkjJv2ew – Anthony Hayward Jul 08 '15 at 07:21
  • 1
    In Excel you can use square brackets around the call to Now to get the Worksheet function version which has sub-second precision. `[Now()]` – HackSlash Jun 25 '18 at 22:34
  • 2
    One of the more elegant solutions :) – Newteq Developer Dec 23 '19 at 09:30
  • This is not a good solution, it causes high CPU usage (1 thread gets maximum usage) and doesn't provide sub 1-second resolution. Use [this solution](https://stackoverflow.com/a/74387976/12287457), it solves both of these issues. – GWD Nov 10 '22 at 11:59
7

You can also pause the current macro context with Application.Wait T which won't block the whole process.

Alex K.
  • 171,639
  • 30
  • 264
  • 288
  • 4
    This only applies to `Excel.Application`. The `Wait` method does not exist for other Office application object models. – C Perkins Apr 07 '19 at 16:36
4
Application.Wait DateAdd("m", 10, Now) ' Wait for 10 Minutes
 Application.Wait DateAdd("s", 10, Now) ' wait for 10 seconds
Andrew Barber
  • 39,603
  • 20
  • 94
  • 123
Babu
  • 41
  • 1
  • 2
    Welcome to Stack Overflow! Thanks for posting your answer! Please be sure to read the [FAQ on Self-Promotion](http://stackoverflow.com/faq#promotion) carefully. Also note that it is *required* that you post a disclaimer every time you link to your own site/product. – Andrew Barber Feb 20 '13 at 11:03
  • @AndrewBarber: Looking at the link, it doesn't look like Babu's site, but rather like a useful read for the SO. Why would you want to delete it? – Peter Albert Feb 20 '13 at 11:16
  • @PeterAlbert It is Babu's site. They have been spamming it a lot lately (multiple of their answers were deleted as spam because they didn't even contain this much info), and it's in their profile here. – Andrew Barber Feb 20 '13 at 20:25
  • 1
    @AndrewBarber - thanks for the info! I have to admit, I would not have noticed! – Peter Albert Feb 20 '13 at 20:29
  • "m" = month; If you want minutes use "n" – MatAff Nov 08 '18 at 18:11
2

With this code Excel not freeze and the CPU usage is low:

Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
Sub Delay(s As Single)
    Dim TimeOut As Single
    TimeOut = Timer + s
    Do While Timer < TimeOut
        DoEvents
        Sleep 1 'With this line the CPU usage is 00 instead of 50 with an absolute error of +1ms and the latency of 1ms.
    Loop
End Sub
  • **This solution has a severe bug:** when `Timer + s > 86400`, which can easily happen if called shortly before midnight, it will never stop sleeping! Also, this solution doesn't work on Mac or in 64bit Windows applications. For a solution that deals with both of these issues, look [here](https://stackoverflow.com/a/74387976/12287457). Also, note that the time resolution of `Sleep` is [not actually 1 millisecond](https://stackoverflow.com/q/9518106/12287457). – GWD Nov 10 '22 at 12:04
2

Pausing an application for 10 seconds:

Application.Wait (Now + TimeValue("0:00:10"))
desertnaut
  • 57,590
  • 26
  • 140
  • 166
  • Hi, is there any reason you can think of why this function would not be available in MS Outlook? For some reason, the Alt+F11 VBA editor does not seem to show it, and it won't work when I run it within functions on Outlook messages (from a Rule). Thanks! – Kalin Oct 18 '19 at 16:51
  • @Kalin look at [this solution](https://stackoverflow.com/a/74387976/12287457) for a cross-platform, cross-app solution that doesn't freeze the application and doesn't cause excessive CPU usage. – GWD Nov 10 '22 at 12:10
1

Here's what you need for cross compatability with 32-bit and 64-bit Windows machines. The delay is in milliseconds, so use 1000 for a 1 second delay.

First, put this above your other Subs/Functions in a module. On a 64-bit machine the line after "#Else" will be highlighted as if there is an error, but this isn't an issue. The code will compile and run.

#If VBA7 Then
    Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
#Else
    Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
#End If

Now you can create a delay like this example with a 1.5 second delay:

Sub ExampleWithDelay()
    Msgbox "This is a message before the delay."
    Sleep 1500    ' delay of 1,000 milliseconds or 1.5 seconds
    Msgbox "This is a message -AFTER- the delay."
End Sub

As noted by @SLaks, this freezes the application (preventing user input) but you can also call DoEvents in a While loop. See the example below. It runs for 10 seconds and allows user interaction.

Every 1/10th of a second it updates the Excel status bar with:

  1. The address of the active cell

  2. A countdown

    Sub ExampleWithDelayInLoop()    ' for MS Excel
    
        Dim thisMessage As String
        Dim countdownText As String
        Dim i As Long
    
        Const TOTAL_SECONDS As Byte = 10
    
        For i = 1 To TOTAL_SECONDS * 10
    
            countdownText = Excel.WorksheetFunction.RoundUp(TOTAL_SECONDS - (i / 10), 0)
            thisMessage = "You selected " & Excel.ActiveCell.Address & Space$(4) & countdownText & " seconds remaining"
    
            ' Show the address of the active cell and a countdown in the Excel status\
            '   bar.
            If Not Excel.Application.StatusBar = thisMessage Then
                Excel.Application.StatusBar = thisMessage
            End If
    
            ' Delay 1/10th of a second.
            '   Input is allowed in 1/10th second intervals.
            Sleep 100
            DoEvents
    
        Next i
    
    
        ' Reset the status bar.
        Excel.Application.StatusBar = False
    End Sub
    
ChrisB
  • 3,024
  • 5
  • 35
  • 61