【问题标题】:Consistently receiving user input through a long-running procedure (DoEvents or otherwise)通过长时间运行的过程(DoEvents 或其他)持续接收用户输入
【发布时间】:2020-03-20 08:44:52
【问题描述】:

彻底取消冗长的 API-Ridden 过程是地狱般的,我正在尝试找出最好的方式来驾驭地狱般的地狱。

我使用的是 excel 2016(手动计算且没有屏幕更新)- 我可能需要一些时间尝试在 2010 年运行该程序,看看未来几天是否有任何问题得到解决(我知道减速)。

随着时间的推移,我的程序LongProcedure 失去了成功使用其取消功能的能力(可能是由于复杂性增加)。它最初是不一致的,需要大量点击垃圾邮件才能取消,现在它完全失败了

设置如下:
首先,LongProcedure 位于类模块 LongClass 中,并检查了是否提前取消的公共属性,以便清理。

Public Sub LongProcedure()
    ' [Set up some things] '
    For Each ' [Item In Some Large Collection (Est. 300 Items)] '
        ' [Some Code (ETA 5 Seconds) Sprinkled with 3-4 DoEvents] '
        ' [Export workbook (ETA 10 Seconds)] '
        If (cancelLongProcedure) Then Exit For
    Next
    ' [Clean up some things] '
    GeneratorForm.Reset ' Let the UserForm know we're finished
End Sub

其次,我有一个从宏中显示的用户窗体,它实例化过程类并运行该过程。它包含一个运行按钮、一个状态标签和一个取消按钮。

Private MyLong As LongClass

Public Sub ButtonRunLongProcedure_Click()
    Set myLong = New LongClass
    myLong.LongProcedure()
End Sub

所以整体问题是双重的。
ExportAsFixedFormat 调用会打开一个“正在发布...”进度条,它会将 excel 冻结大约十秒钟 - 很好。在我的所有努力中,我还没有找到一种在这种情况下处理用户输入的方法。
最重要的是,DoEvents 调用似乎不再执行任何操作来允许单击取消按钮。该过程不一致地冻结 excel、其他打开程序的选项卡,并且(当不冻结时)更新状态标签。

我试过了:

  • 将 DoEvents 附加到 SetStatusLabel 方法而不是喷洒 - 虽然表单仍然经常冻结,否则它会持续更新状态标签(但仍然不允许取消按钮)
  • 使用 winAPI Sleep 代替 DoEvents,延迟时间为 1、5、10、50 和 250 毫秒 - 表单在没有 doevents 的情况下完全停止更新,并且两者都冻结得更多。
  • 使用Do While 循环持续运行DoEvents 一秒钟(冻结)
  • 重写 QueryClose 以取消表单。这个帮助很大。出于某种原因,可以比用户表单按钮更一致地单击关闭 [x] 按钮 - 仍然不像我想要的那样一致。问题?在发布期间,Excel 停止响应,因此,如果您单击关闭按钮两次,现代 Windows 将结束该过程...而不进行清理。
  • 使用 Application.OnTime 定期调用 DoEvents。整体情况似乎没有改善
  • Alt-Tab 键。不完全是。出于某种原因,虽然 alt-tabbing 有时只会使用户窗体更难冻结,但有时它会使其停止冻结并更新。

这是一个我愿意做大量重构工作的问题,包括将长过程的想法分解为单独的方法、最初执行设置以及清理类终止。我正在寻找能够提供一致结果的东西。 - 我将接受从 excel 版本到 excel 设置到重构到 winAPI 调用的任何内容。

感谢您对此的任何见解。

【问题讨论】:

    标签: excel vba userform


    【解决方案1】:

    事实证明,只需将一些有用的改进结合在一起,再加上一项新改进,就会产生重大影响。

    • QueryClose 取决于个人喜好。保留它以捕获更多终止,保留它以确保用户使用新解决方案
    • 坚持在您认为合乎逻辑的地方喷洒doEvents(不仅仅是在状态栏更新时 - 比如在Application.Calculate 调用之前和之后)
    • 尽可能优化长时间运行的流程,避免调用 excel

    而且,最重要的是

    • 集成的取消键功能(默认为CTRL+Break)比用户窗体按钮表单关闭按钮响应速度明显更快,没有意外结束excel的机会任务。

    这是为成品抛光的过程

    首先,设置一个debugMode,或者相反的handleErrors,模块级变量来控制是否实现break-to-cancel和错误处理。 (错误处理会使你的代码更难调试,所以你会喜欢这个开关)

    如果您的进程正在处理错误,您需要将Application.EnableCancelKey 设置为xlErrorHandlerOn Error GoTo [ErrorHandlingLabel]。错误处理标签应该直接在清理之前,并立即将EnableCancelKey设置为xlDisabled以避免错误。在继续执行清理步骤之前,您的处理程序应检查存储的 Err.Number 并采取相应措施。

    确保如果您在脚本中遵循任何其他复杂的 vba(例如在带有 UDF 的工作表上使用 Application.Calculate),请事先设置 On Error GoTo 0,然后设置 On Error GoTo [ErrorHandlingLabel],以避免捕获单元绑定错误。

    不幸的是,缺点是要使用户体验始终具有可读性,您必须将取消键留在xlDisabled,直到表单关闭。

    在代码中:

    Public Sub LongProcedure()
        If handleErrors Then
            On Error GoTo ErrorHandler
            Application.EnableCancelKey = xlErrorHandler
        End If
        ' [Set up some things] '
        For Each ' [Item In Some Large Collection (Est. 300 Items)] '
            ' [Some Code (ETA 5 Seconds) Sprinkled with 3-4 DoEvents] '
            ' [Export workbook (ETA 10 Seconds)] '
        Next
    ErrorHandler:
        If handleErrors Then
            Application.EnableCancelKey = xlDisabled
            If (Err.Number <> 0 And Err.Number <> 18) Then
                MsgBox Err.Description, vbOKOnly, "Error " & CStr(Err.Number)
            End If
            Err.Clear
            On Error GoTo 0
        End If
        ' [Clean up some things] '
        GeneratorForm.Reset ' Let the UserForm know we're finished
    End Sub
    

    在用户窗体中

    Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
        If MyLong.handleErrors Then Application.EnableCancelKey = xlInterrupt
    End Sub
    

    一个小提示,此方法可能会产生一些您没有预料到的错误,因为执行会直接跳转到指定的标签。您的清理代码需要从一开始就实例化所需的变量。

    总的来说,一旦这些问题得到解决,此设置可确保用户可以根据需要多次点击CTRL+Break,而不会导致崩溃或弹出窗口。

    【讨论】:

      猜你喜欢
      • 2018-02-22
      • 2018-08-29
      • 2014-09-14
      • 2015-07-17
      • 2011-08-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多