DoEvents的应用及注意事项 转让控制权,以便让操作系统处理其它的事件。 DoEvents
函数会返回一个 Integer,以代表 Visual Basic 独立版本中打开的窗体数目,例如,Visual Basic,专业版,在其它的应用程序中,DoEvents 返回 0。 DoEvents 会将控制权传给操作系统。当操作系统处理完队列中的事件,并且在 SendKeys 队列中的所有键也都已送出之后,返回控制权。 DoEvents 对于简化诸如允许
用户取消一个已启动的过程 — 例如搜寻一个
文件 — 特别有用。对于长时间过程,放弃控制权最好使用定时器或通过委派任务给 ActiveX EXE 部件来完成。以后,任务还是完全独立于应用程序,多任务及时间片由操作系统来处理。 小心 确保以 DoEvents 放弃控制权的过程,在第一次 DoEvents 返回之前,不能再次被其他部分的
代码调用;否则会产生不可预料的结果。此外,如果其它的应用程序可能会和本过程以不可预知的方式进行交互操作,那么也不要使用 DoEvents,因为此时不能放弃控制权。 使用 DoEvents 尽管 Timer 事件是
后台处理的最好工具,对耗时极多的任务,情况更是如此,但是,DoEvents
函数还是提供了一种取消任务的简便
方法。例如,下列
代码将
显示一个 "Process" 按钮,单击这个按钮时,它将变成 "Cancel" 按钮。再次单击按钮又将中断正在执行的任务。 '此按钮
标题是 "Process" Private Sub Command1_Click() '过程的所有实例都共享静态变量。 Static blnProcessing As Boolean Dim lngCt As Long Dim intYieldCt As Integer Dim dblDummy As Double '按下按钮时,检测是否在处理 If blnProcessing Then '如果正在处理,则取消 blnProcessing = False Else Command1.Caption = "Cancel" blnProcessing = True lngCt = 0 '执行一百万次浮点乘法计算。每一千次后,检测是否要取消。 Do While blnProcessing And (lngCt < 1000000) For intYieldCt = 1 To 1000 lngCt = lngCt + 1 dblDummy = lngCt * 3.14159 Next intYieldCt 'DoEvents 语句允许其它事件发生,
包括第二次按此按钮。 DoEvents Loop blnProcessing = False Command1.Caption = "Process" Msg
Box lngCt & " multiplications were performed" End If End Sub DoEvents 将控制切换到操作环境内核。只要此环境中的所有应用程序都有机会响应待处理事件,应用程序就又恢复控制。这不会使应用程序放弃焦点,但会使
后台事件能够得到处理。 这种妥协的结果可能并不总是达到预期目标。例如,下述 Click 事件
代码在单击按钮后要一直等候十秒钟,而后才
显示一条信息。如果在按钮正在等待期间单击它,则将以相反顺序完成单击操作。 Private Sub Command2_Click() Static intClick As Integer Dim intClickNumber As Integer Dim dblEndTime As Double '每次单击按钮时 '赋予唯一数值。 intClick = intClick + 1 intClickNumber = intClick '等待十秒。 dblEndTime = Timer + 10# Do While dblEndTime > Timer '不做任何事情,仅仅允许 '其它应用程序处理 '它们的事件。 DoEvents Loop Msg
Box "Click " & intClickNumber & " is finished" End Sub 对于通过 DoEvents 放弃控制的事件过程,有时可能希望防止在 DoEvents 返回之前重新
调用这一过程。否则将无穷无尽地
调用该过程,直到系统资源消耗殆尽。可暂时
禁止控件,或象上例一样,使用一个静态的“标志”变量防止此事发生。 在使用全局数据时避免 DoEvents 当一个
函数已通过 DoEvents 放弃控制时,可相当安全地再次
调用函数。例如,下一过程将检测质数并用 DoEvents 语句周期地启动其它应用程序处理事件: Function PrimeStatus (TestVal As Long) As Integer Dim Lim As Integer PrimeStatus = True Lim = Sqr(TestVal) For I = 2 To Lim If TestVal Mod I = 0 Then PrimeStatus = False Exit For End If If I Mod 200 = 0 Then DoEvents Next I End Function 该
代码中每重复 200 次就
调用一次 DoEvents 语句。这样一来,当该环境的其余部分对事件作出响应时,只要有必要,PrimeStatus 过程就可继续计算。 考虑在
调用 DoEvents 期间发生的事情。在其它窗体和应用程序处理事件时将暂停执行应用程序
代码。这些事件之一有可能是一个按钮单击操作,它将再次启动 PrimeStatus 过程。 这将导致重新进入 PrimeStatus 过程的,但是,因为在
函数每次出现时,堆栈都为其参数和局部变量分配了空间,所以重入不会引发冲突。当然,如果过多
调用 PrimeStatus,则可能出现“溢出堆栈空间”
错误。 如果 PrimeStatus 使用或改变模块级变量或全局数据,情况就会完全不同。此时,在 DoEvents 能够返回之前执行 PrimeStatus 的另一个实例,这将导致模块数据或全局数据的值完全不同于它们在
调用 DoEvents 之前的值。于是,PrimeStatus 的结果将会难以预料。 VB中DoEvents的注意事项 对与再大循环里 加入 DoEvents却是可以使应用程序响应其他程序,但是 DoEvents也会很大程度上影响循环的速度,所以一般情况我这样用 If GetInputState Then DoEvents 虽然多了个判断语句,但速度上确快了很多 比较以下
代码代码就知道了速度的差别 Private Declare Function GetInputState Lib "user32" () As Long Private Declare Function GetTickCount Lib "kernel32" () As Long Private Sub Command1_Click() Dim s As Long s = GetTickCount For i = 1 To 1000000 DoEvents Next Msg
Box GetTickCount - s End Sub Private Sub Command2_Click() Dim s As Long s = GetTickCount For i = 1 To 1000000 If GetInputState Then DoEvents Next Msg
Box GetTickCount - s End Sub