在VBA中是否有Thread.Sleep()的等效功能?

46

在Access VBA中,是否有与Thread.Sleep()相当的功能?


2
这是一个Access常见问题解答,你会发现http://mvps.org/Access/是一个重要的来源,可以回答许多常见问题。 - David-W-Fenton
10个回答

63
Declare Sub Sleep Lib "kernel32" Alias "Sleep" _
(ByVal dwMilliseconds As Long)
使用以下语法调用Sleep函数:
Sub Sleep()
Sleep 1000 'Implements a 1 second delay
End Sub 

啊,我自己找到了。谢谢! - Johnno Nolan
1
我应该注意,在Excel 2007中,我能够直接调用Sleep而不需要使用包装的VBA子程序。 - Charles Wood
2
声明语句应该在模块中,以避免出现“Declare statements are not allowed as Public members of object”错误。 - Joshua Stafford

8

不使用 kernel32 的另一种方法:

Dim started As Single: started = Timer

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

8
所有其他让Excel等待的方法都会导致Excel完全无响应。确保具有响应性用户界面的方法是调用此等待子程序并等待秒数。
    Sub Wait(seconds As Integer)
      Dim now As Long
      now = Timer()
      Do
          DoEvents
      Loop While (Timer < now + seconds)
    End Sub

1
使用这种方法的问题在于,DoEvents最终会使用几乎所有的CPU时间。 - phrebh

6

需要进行一些修改才能使代码正常工作。以下是已经修正的代码。

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

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

4

我在Excel中使用这个功能,效果非常好:

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

DateAdd() 是一个函数,它基于 Now() (在这种情况下 - 您可以使用其他值作为参数)设置时间,"s" 是时间度量(在本例中为秒),并增量为 1。因此,在这里,函数调用告诉应用程序等待 1 秒。

另请参阅有关使用 DateAdd 函数的更多详细信息。


@MAW74656 好的,我确实说的是在Excel中,我没有在Access中测试过。你说“as-is”,你知道有什么解决方法吗? - Gaffi

3
如果您使用Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long),您可能会在对象模块中遇到此错误。

enter image description here

如果需要,您可以将其声明为私有:
私有声明子程序睡眠库“kernel32”(ByVal dwMilliseconds As Long)

2

跨平台解决方案

被接受的答案在64位VBA中无法使用。此外,它无法在Mac上工作

感谢@Cristian Buse在您的评论中指出Mac的兼容性!

实现完全的Mac兼容性需要导入特定于平台的库函数以挂起线程执行,即Mac上为usleep,Windows上为Sleep。由于usleep将其参数以微秒为单位传递,而Sleep使用毫秒,因此需要声明一个自定义的Sub来处理转换。

导入库函数并声明Sub可以按以下方式完成。

这个解决方案是我和Cristian Buse共同合作改编他的原始方案而来,以避免整数溢出并允许在 Mac 上使用超过&HFFFFFFFF微秒(约71分钟)的Sleep时间:

#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
    #If VBA7 Then
        Private Declare PtrSafe Sub MSleep Lib "kernel32" Alias "Sleep" (ByVal dwMilliseconds As Long)
    #Else
        Private Declare Sub MSleep Lib "kernel32" Alias "Sleep" (ByVal dwMilliseconds As Long)
    #End If
#End If
'Sub providing a Sleep API consistent with Windows on Mac (argument in ms)
'Authors: Guido Witt-Dörring, https://dev59.com/L3RB5IYBdhLWcg3w6bYE#74262120
'         Cristian Buse,      https://dev59.com/9sLra4cB1Zd3GeqPBhQO#71176040
Public Sub Sleep(ByVal dwMilliseconds As Long)
    #If Mac Then 'To avoid overflow issues for inputs > &HFFFFFFFF / 1000:
        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
    #Else 'Windows
        MSleep dwMilliseconds
    #End If
End Sub

现在 Sleep 将在 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://dev59.com/kXI_5IYBdhLWcg3wEOrq#74387976
End Sub

请注意:

  1. Sleep 函数虽然以毫秒为单位接受参数,但其分辨率实际上不是1毫秒
  2. 在 Mac 上,最大的 usleep 持续时间受到 MAX_UINT = &HFFFFFFFF 微秒或约 4294.97 秒(~71 分钟)的限制。这就是为什么 Mac 上的自定义 Sleep 子程序会对输入值 dwMilliseconds > &H418937 调用多次 usleep,以避免整数溢出问题。
  3. 在 Windows 上(以及在具有自定义 Sleep 子程序的 Mac 上),最大的 Sleep 持续时间受到 MAX_UINT 毫秒或约 4294967.3 秒(~49.71 天)的限制。
  4. 在 Windows 和 Mac 上,调用 Sleep 并传递大于 MAX_LONG = &H7FFFFFFF= 2147483647)的毫秒值需要传递负的 VBA Long 值,通过像这样调用它来实现最大的 Sleep 持续时间:Sleep -1(=Sleep &HFFFFFFFF

请注意,VBA 中这种暂停执行的方式具有严重的缺点!

最重要的是,所有 Microsoft Office 应用程序都在与主用户界面相同的线程中运行 VBA 代码。这意味着,调用 Sleep 实际上会冻结整个应用程序(在任务管理器中将显示为“未响应”)。除非等待时间过去或强制退出应用程序并重新启动它,否则没有办法从此状态恢复。Excel 的 Application.Wait 也存在同样的问题。虽然在这种情况下应用程序不会在任务管理器中显示为 not responding,但对于用户来说,它仍然无响应。

解决此问题的方法是在循环中调用 DoEvents,正如其他人已经指出的那样。然而,这又带来了另一个问题。因为应用程序将尝试尽可能快地执行 VBA 代码,所以 DoEvents 以最大可达速率调用,从而完全占用单个线程上的 CPU,导致高,不必要的 CPU 和电源使用,并潜在地减慢 UI 中的其他更重要任务。

要了解暂停VBA执行的最佳方法,请查看此答案


1
为了完整起见,也许需要更新适用于 MAC 的版本。示例在此处。顺便说一句,我也使用相同的技术 :) - Cristian Buse

2

可以从Access VBA使用Excel的Wait()过程。

第一步是确保在项目中引用了Excel库。

完成后,以下代码将等待十秒钟:

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

1

添加

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

希望这有所帮助:)

1
我能想到的唯一原因是,如果您已经在当前进程的模块或类中使用了函数名称“Sleep”,那么添加函数头可能会引起“问题”。 - Anonymous Type

0

如果代码正在表单中执行,您可以使用表单的内置计时器。

使用以下代码启动表单的计时器:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接