“DoEvents”在 vb6 中有什么作用?为什么我会收到错误消息“堆栈空间不足”?这是什么意思 ?
4 回答
DoEvents() 允许处理其他 Windows 消息。
您收到堆栈空间不足错误的原因可能是因为 DoEvents() 允许发生再次调用您的代码的事件,该代码再次调用 DoEvents(),依此类推,直到堆栈空间跟踪所有这些的返回地址来电,已经用完了。
一般来说,我不建议使用 DoEvents() ,因为这些问题以及它违反了 Windows 的整体事件驱动设计的事实。
查看 DoEvents 的一个稍微不同的方式是它刷新事件队列中的事件。如果您的子程序或函数触发了一个事件,则该事件处理程序将成为一个子程序,一旦您的子程序/函数完成,就会立即运行。DoEvents 说现在运行该事件处理程序子程序,而不是等到您的子程序结束。
虽然我在精神上同意 Jonathon 关于不使用 DoEvents 的观点,但我会缓和他的说法,说我只推荐在您确切知道原因的情况下使用它,并且知道以这种方式更改事件队列顺序的所有影响。大多数情况下,当您想在子例程完成执行之前从子例程的上下文中以某种方式更新屏幕时,会指示 DoEvents。
例如,当您使用 ProgressBar 控件时。假设您正在遍历数千条记录,并希望通过更新进度条向用户提供有关您已经走了多远的反馈。您可能会每隔一百条记录中断一次循环并更改进度条控件上的值。但是(除非您对此进行处理)在进度条的更改事件处理程序运行之前,您不会在屏幕上看到更改,并且该处理程序在您的子程序完成执行之前不会运行。它只会被放入事件队列中。强制更改事件立即运行、暂停您的子程序的方法是调用 DoEvents。这将从队列中刷新所有现有事件——在这种情况下,你的进度条的更改事件——并将更新屏幕上的进度条控件。
现在,“堆栈空间不足”基本上意味着您陷入了函数调用的无限循环。导致这种情况的最基本方法是:
Public sub MySub()
MySub
End Sub
然后从某个地方调用 MySub。你会得到一个堆栈空间不足的错误。如果您查看调用堆栈,您会看到对 MySub 的很长的调用行。
一个众所周知的现实世界的例子会发生在旧版本的 VB 中:
Public Sub TextBoxArray_LostFocus(index as Integer)
If TextBoxArray(index) = "" Then
TextBoxArray(index).SetFocus
MsgBox "Please enter a value"
End If
End Sub
这种情况假定一个名为 TextBoxArray 的 TextBox 控件数组有两个成员。现在,如果用户从第一个(索引 0)开始并移动到第二个(索引 1),那么索引 0 的 LostFocus 事件将触发。但是,VB 也会在内部将焦点设置到索引 1 框。然后代码会将焦点设置回索引 0,触发索引 1 的 LostFocus 事件!你陷入了一个循环。他们在 VB5 或 6 中通过等待设置焦点直到 LostFocus 事件执行完成来修复该问题。
我要澄清 Johnathon 的答案,因为它泵送 VB 消息循环并允许 VB 运行时处理 Windows 消息,这与允许 Windows 处理其事件的睡眠相反(在多核 CPU 和真正的多任务操作系统的世界中不是必需的)但是当编写 VB6 时,Windows 9x 是占主导地位的操作系统,并且其中只有 DoEvents 的硬循环会将 CPU 使用率飙升至 100%)。所以看到像
While fDoneFile = False
DoEvents
Sleep 55
Wend
是整个 VB6 世界的常见模式。
如其他地方所述,DoEvents 允许您的应用程序中的其他事件触发。这是一个示例,说明如何在没有“堆栈空间不足”问题的情况下使用 DoEvents。这可以确保您不会通过使用布尔值指示代码正在运行来多次运行代码。
Sub Example()
'Create static variable to indicate the sub is running.
Static isRunning As Boolean
'Exit the sub if isRunning
If isRunning Then Exit Sub
'Indicate sub is running
isRunning = True
'Sub does stuff
DoEvents
'Ends up calling sub again
Example 'Added just to prove via testing.
'Indicate sub is no longer runningrunning
isRunning = False
End Sub