取消 VB6.0 中没有 DoEvents 的长时间运行的进程



是否可以在不使用DoEvents的情况下取消VB6.0中长时间运行的进程?

例如:

for i = 1 to someVeryHighNumber
    ' Do some work here '
    ...
    if cancel then
        exit for
    end if
next
Sub btnCancel_Click()
    cancel = true
End Sub

我假设我需要在"如果取消那么......"之前有一个"DoEvents"有没有更好的方法?已经有一段时间了...

不,你做对了,你肯定希望 DoEvents 在你的循环中。

如果将DoEvents放在主循环中,并发现这会减慢处理速度,请尝试调用 Windows API 函数GetQueueStatus(比 DoEvents 快得多)以快速确定是否有必要调用 DoEvents。 GetQueueStatus告诉您是否有任何事件要处理。

' at the top:
Declare Function GetQueueStatus Lib "user32" (ByVal qsFlags As Long) As Long
' then call this instead of DoEvents:
Sub DoEventsIfNecessary()
    If GetQueueStatus(255) <> 0 Then DoEvents
End Sub

不可以,您必须使用 DoEvents,否则所有 UI、键盘和计时器事件都将在队列中等待。

您唯一能做的是每 1000 次迭代或类似迭代调用一次 DoEvents。

"for"循环是否在 GUI 线程中运行? 如果是这样,是的,你需要一个DoEvents。 您可能希望使用单独的线程,在这种情况下,不需要 DoEvents。 您可以在 VB6 中执行此操作(不简单)。

这是 VB6 中异步后台处理的一个非常标准的方案。(例如,它在Dan Appleman的书和Microsoft的VB6样本中。您可以创建一个单独的 ActiveX EXE 来完成工作:这样,工作就会自动在另一个线程上,在一个单独的进程中(这意味着您不必担心变量被践踏)。

  • VB6 ActiveX EXE 对象应公开一个事件 CheckQuitDoStuff()。这需要一个名为 Quit 的 ByRef 布尔值。
  • 客户端在 ActiveX EXE 对象中调用 StartDoStuff。此例程在隐藏窗体上启动计时器并立即返回。这将取消阻止调用线程。计时器间隔非常短,因此计时器事件会快速触发。
  • 计时器
  • 事件处理程序禁用计时器,然后回调到 ActiveX 对象 DoStuff 方法。这将开始冗长的处理。
  • DoStuff 方法定期引发 CheckQuitDoStuff 事件。客户端的事件处理程序检查特殊标志,并在需要中止时设置 Quit True。然后,DoStuff 中止计算,如果 Quit 为 True,则提前返回。

这种方案意味着客户端实际上不需要是多线程的,因为在"DoStuff"发生时调用线程不会阻塞。棘手的部分是确保DoStuff以适当的时间间隔引发事件 - 太长,你不能在你想退出的时候退出:太短,并且你不必要地减慢了DoStuff的速度。此外,当 DoStuff 退出时,它必须卸载隐藏的表单。

如果 DoStuff 确实设法在中止之前完成了所有工作,您可以引发一个不同的事件来告诉客户端作业已完成。

您可以在单独的线程上启动它,但在 VB6 中,这是一个皇家痛苦。 DoEvents应该有效。 这是一个黑客,但VB6也是如此(10年的VB老兵在这里说话,所以不要贬低我)。

长时间运行的任务划分为量子。 此类任务通常由一个简单的循环驱动,因此将其切成 10、100、1000 等迭代。 使用 Timer 控件,每次触发时执行任务的一部分,并随时保存其状态。 首先,设置初始状态并启用计时器。完成后,禁用计时器并处理结果。

您可以通过更改每个量程完成的工作量来"调整"这一点。 在 Timer 事件处理程序中,可以检查"取消"并根据需要提前停止。 通过将工作负荷和计时器捆绑到具有 T已完成 事件的用户控件中,可以使这一切更整洁。

当我需要它时,这对我很有用。它检查用户是否已按下 esc 键退出循环。

请注意,它有一个非常大的缺点:它会检测用户是否在任何应用程序上点击了转义键 - 而不仅仅是你的。但是,当您想给自己一种中断长时间运行的循环的方法,或者一种按住 shift 键绕过一些代码的方法时,这是一个很好的开发技巧。

Option Explicit
Private Declare Function GetAsyncKeyState Lib "user32" (ByVal nVirtKey As Long) As Integer
Private Sub Command1_Click()
    Do
        Label1.Caption = Now()
        Label1.Refresh
        If WasKeyPressed(vbKeyEscape) Then Exit Do
    Loop
    Label1.Caption = "Exited loop successfully"
End Sub
Function WasKeyPressed(ByVal plVirtualKey As Long) As Boolean
    If (GetAsyncKeyState(plVirtualKey) And &H8000) Then WasKeyPressed = True
End Function

GetAsyncKeyState 的文档在这里:

http://msdn.microsoft.com/en-us/library/ms646301(VS.85).aspx

编辑结果发现MSDN文章有缺陷,该技术

不起作用:(

下面是一篇关于使用 .NET BackgroundWorker 组件从 VB6 中在另一个线程上运行任务的文章。

最新更新