【问题标题】:Timing Delays in VBAVBA 中的时序延迟
【发布时间】:2011-10-21 02:03:41
【问题描述】:

我希望我的代码延迟 1 秒。下面是我试图使这种延迟的代码。我认为它会轮询操作系统的日期和时间并等到时间匹配。我遇到了延迟问题。我认为它不会轮询与等待时间匹配的时间,它只是坐在那里并冻结。它只冻结了我运行代码的大约 5% 的时间。我想知道 Application.Wait 是否有办法检查轮询时间是否大于等待时间。

   newHour = Hour(Now())
   newMinute = Minute(Now())
   newSecond = Second(Now()) + 1
   waitTime = TimeSerial(newHour, newMinute, newSecond)
   Application.Wait waitTime

【问题讨论】:

  • 我不清楚您的具体问题是什么。是结冰吗?是因为冻结时间低于 1 秒吗?

标签: vba timer wait pause timedelay


【解决方案1】:

如果您在 Excel VBA 中,您可以使用以下内容。

Application.Wait(Now + TimeValue("0:00:01"))

(时间字符串应类似于 H:MM:SS。)

【讨论】:

  • 标签上写着 VBA,它不包括 Excel。等待在 MS Access 中不可用,例如,可能还有许多其他使用 VBA 的应用程序。
  • 哇,你说得对,我很抱歉。我不知道为什么我在想 Excel。也许是因为我从 Excel 帮助中认出了原始代码。不管怎样,希望看到这一点的任何人都会看到它至少在 Excel 中实现起来很简单。我不知道为什么他们没有在 Access 中实现它。但你是对的,我也遇到了这个问题。
  • 无论如何,OP 仍然使用 Wait 方法,因此他可以访问它,不管他在哪里,他有Application.Wait,这比投票率更高的答案简单得多
  • 我认为答案的语气是责备cmets的消极情绪。
  • 我并没有居高临下的意思,只是对复杂方法的一般批评,而不是研究已经使用的方法。节省大量时间和精力。
【解决方案2】:

我将这个小函数用于 VBA。

Public Function Pause(NumberOfSeconds As Variant)
    On Error GoTo Error_GoTo

    Dim PauseTime As Variant
    Dim Start As Variant
    Dim Elapsed As Variant

    PauseTime = NumberOfSeconds
    Start = Timer
    Elapsed = 0
    Do While Timer < Start + PauseTime
        Elapsed = Elapsed + 1
        If Timer = 0 Then
            ' Crossing midnight
            PauseTime = PauseTime - Elapsed
            Start = 0
            Elapsed = 0
        End If
        DoEvents
    Loop

Exit_GoTo:
    On Error GoTo 0
    Exit Function
Error_GoTo:
    Debug.Print Err.Number, Err.Description, Erl
    GoTo Exit_GoTo
End Function

【讨论】:

  • 这会有所不同。 Application.Wait 持有主 UI 线程,但会继续执行后台操作,例如重新计算
  • 我只需要让代码暂停而不等待 24 小时。谢谢您的帮助。我还注意到时间只有 2 位小数。这是否意味着您可以等待的最短时间为 0.01 秒?
  • 你为什么把它变成一个函数而不是一个子函数?它不返回结果。
  • 注意@SteveMallory 并警告其他人:解决午夜问题的编辑是错误的。见meta.stackoverflow.com/questions/408871/…。
  • @NewSites 然后将您的更正作为新答案发布。至少您应该在对此答案本身的评论中包含您的推理
【解决方案3】:

您可以将其复制到一个模块中:

Sub WaitFor(NumOfSeconds As Long)
Dim SngSec as Long
SngSec=Timer + NumOfSeconds

Do while timer < sngsec
DoEvents
Loop

End sub

以及每当您想应用暂停写入时:

Call WaitFor(1)

希望对你有帮助!

【讨论】:

  • 不是假设每个 DoEvents 都需要一秒钟的时间来执行吗?
  • Tiago 我不确定我理解你的意思。秒数在括号中声明。
  • 没关系,现在才注意到这个Timer 是一个内部VBA 函数(我不知道)。还注意到这与@Steve Mallory 提出的解决方案几乎相同。不管怎样,谢谢你让我知道这个新功能 =]
【解决方案4】:

你试过使用睡眠吗?

有一个例子HERE(复制如下):

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

Private Sub Form_Activate()    

frmSplash.Show
DoEvents
Sleep 1000
Unload Me
frmProfiles.Show

End Sub

请注意,它可能会在选定的时间内冻结应用程序。

【讨论】:

  • 这在 x64 Office 上如果没有微小的调整就无法工作,而且 excel 中的 Timer 功能是 Single 所以在大多数答案中允许不到一秒
  • 这可能是所有其他实现在幕后所做的。 +1 Win32 API。
【解决方案5】:

只要项目有 Microsoft Excel XX.X 对象reference included,Access 就可以始终使用 Excel 过程:

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

【讨论】:

    【解决方案6】:

    您的代码只创建一个没有日期的时间。如果您的假设是正确的,即当它运行 application.wait 时实际已经达到该时间,它将完全等待 24 小时。我也有点担心 now() 多次调用(可能会不同?)我会将代码更改为

     application.wait DateAdd("s", 1, Now)
    

    【讨论】:

      【解决方案7】:

      Steve Mallorys 回答的另一种变体,我特别需要 excel 才能在等待时跑掉并做一些事情,而 1 秒太长了。

      'Wait for the specified number of milliseconds while processing the message pump
      'This allows excel to catch up on background operations
      Sub WaitFor(milliseconds As Single)
      
          Dim finish As Single
          Dim days As Integer
      
          'Timer is the number of seconds since midnight (as a single)
          finish = Timer + (milliseconds / 1000)
          'If we are near midnight (or specify a very long time!) then finish could be
          'greater than the maximum possible value of timer. Bring it down to sensible
          'levels and count the number of midnights
          While finish >= 86400
              finish = finish - 86400
              days = days + 1
          Wend
      
          Dim lastTime As Single
          lastTime = Timer
      
          'When we are on the correct day and the time is after the finish we can leave
          While days >= 0 And Timer < finish
              DoEvents
              'Timer should be always increasing except when it rolls over midnight
              'if it shrunk we've gone back in time or we're on a new day
              If Timer < lastTime Then
                  days = days - 1
              End If
              lastTime = Timer
          Wend
      
      End Sub
      

      【讨论】:

      • 感谢这个功能,它是这里最完整的,可以满足几天的需求。我投了赞成票。
      【解决方案8】:

      定时器功能同样适用于Access 2007、Access 2010、Access 2013、Access 2016、Access 2007 Developer、Access 2010 Developer、Access 2013 Developer。插入此代码以暂停一定的秒数

      T0 = Timer
      Do
          Delay = Timer - T0
      Loop Until Delay = 1 'Change this value to pause time in second
      

      【讨论】:

        【解决方案9】:

        我使用了 Steve Mallory 的答案,但我担心计时器永远不会或至少有时不会达到 86400 或 0(零)尖锐(MS Access 2013)。所以我修改了代码。我将午夜条件更改为“If Timer >= 86399 Then” 并添加了循环“Exit Do”的中断,如下所示:

        Public Function Pause(NumberOfSeconds As Variant)
            On Error GoTo Error_GoTo
        
            Dim PauseTime As Variant
            Dim Start As Variant
            Dim Elapsed As Variant
        
            PauseTime = NumberOfSeconds
            Start = Timer
            Elapsed = 0
            Do While Timer < Start + PauseTime
                Elapsed = Elapsed + 1
                If Timer >= 86399
                    ' Crossing midnight
                    ' PauseTime = PauseTime - Elapsed
                    ' Start = 0
                    ' Elapsed = 0
                    Exit Do
                End If
                DoEvents
            Loop
        
        Exit_GoTo:
            On Error GoTo 0
            Exit Function
        Error_GoTo:
            Debug.Print Err.Number, Err.Description, Erl
            GoTo Exit_GoTo
        End Function
        

        【讨论】:

        • 如果午夜被击中,这将不会等待指定的秒数。它解决了问题,但表现不如预期
        【解决方案10】:

        在接受的答案中处理午夜是错误的。它测试Timer = 0,这几乎不会发生。它应该改为测试Timer &lt; Start。另一个答案尝试更正Timer &gt;= 86399,但该测试在慢速计算机上也可能失败。

        下面的代码可以正确处理午夜(比Timer &lt; Start 复杂一点)。它也是一个子函数,而不是函数,因为它不返回值,而变量是单数,因为它们不需要变体。

        Public Sub pPause(nPauseTime As Single)
        
        ' Pause for nPauseTime seconds.
        
        Dim nStartTime As Single, nEndTime As Single, _
            nNowTime As Single, nElapsedTime As Single
        
        nStartTime = Timer()
        nEndTime = nStartTime + nPauseTime
        
        Do While nNowTime < nEndTime
            nNowTime = Timer()
            If (nNowTime < nStartTime) Then     ' Crossed midnight.
                nEndTime = nEndTime - nElapsedTime
                nStartTime = 0
              End If
            nElapsedTime = nNowTime - nStartTime
            DoEvents    ' Yield to other processes.
          Loop
        
        End Sub
        

        【讨论】:

          【解决方案11】:

          在 Windows 上,计时器返回百分之一秒...大多数人只使用秒,因为在 Macintosh 平台上,计时器返回整数。

          【讨论】:

            【解决方案12】:

            感谢 Steve Mallroy。

            我在 Word 中遇到了午夜问题,下面的代码对我有用

            Public Function Pause(NumberOfSeconds As Variant)
             '   On Error GoTo Error_GoTo
            
                Dim PauseTime, Start
                Dim objWord As Word.Document
            
                'PauseTime = 10 ' Set duration in seconds
                PauseTime = NumberOfSeconds
                Start = Timer ' Set start time.
            
                If Start + PauseTime > 86399 Then 'playing safe hence 86399
            
                Start = 0
            
                Do While Timer > 1
                    DoEvents ' Yield to other processes.
                Loop
            
                End If
            
                Do While Timer < Start + PauseTime
                    DoEvents ' Yield to other processes.
                Loop
            
            End Function
            

            【讨论】:

              【解决方案13】:

              对于 MS Access:启动带有 Me.TimerInterval 集和 Form_Timer 事件处理程序的隐藏表单。将要延迟的代码放入 Form_Timer 例程中 - 每次执行后退出例程。

              例如:

              Private Sub Form_Load()
                  Me.TimerInterval = 30000 ' 30 sec
              End Sub
              
              Private Sub Form_Timer()
              
                  Dim lngTimerInterval  As Long: lngTimerInterval = Me.TimerInterval
              
                  Me.TimerInterval = 0
              
                  '<Your Code goes here>
              
                  Me.TimerInterval = lngTimerInterval
              End Sub
              

              “您的代码在此处”将在表单打开后 30 秒和每次后续执行后 30 秒执行。

              完成后关闭隐藏表单。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2018-07-18
                • 2022-11-03
                • 2020-01-16
                • 2021-11-03
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多