干净利落地取消一个冗长的API过程是很糟糕的,我正试图找到最佳的方法来导航这个坏消息。
我使用的是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
其次,我从宏中显示了一个UserForm,该宏实例化过程类并运行过程。它包含一个“运行”按钮、一个状态标签和一个“取消”按钮。
Private MyLong As LongClass
Public Sub ButtonRunLongProcedure_Click()
Set myLong = New LongClass
myLong.LongProcedure()
End Sub
因此,总体上这个问题是双重的。
ExportAsFixedFormat
调用打开一个“发布.”进度条,冻结excel大约10秒-很好。在我所有的努力中,我还没有找到一种处理用户输入的方法。
最重要的是,DoEvents
调用似乎不再做任何允许单击cancel按钮的操作。此过程不一致地冻结excel、选项卡到其他打开的程序中,并且(在不冻结时)更新状态标签。
我试过的:
在button)
Sleep
来代替DoEvents
,并且除了延迟为1、5、10、50和250 in的DoEvents
之外,表单完全停止了更新而没有执行事件,而且两者都冻结了更多。
Do While
循环不断地运行DoEvents
,以便第二次运行DoEvents
来取消表单。这个很有帮助。由于某些原因,close x按钮可以比userform按钮更一致地单击--仍然不像我想的那样一致。问题出在哪里?在发布期间,Excel将停止响应,因此,如果您单击“关闭”按钮两次,现代窗口将结束此过程。如果没有cleanup.
这是我愿意做的一个重要的重构工作,包括将长过程的想法分解成单独的方法,最初执行设置,以及在类终止时进行清理。我在寻找能提供一致结果的东西。-我将接受从excel版本到excel设置再到winAPI调用的任何东西。
谢谢你对这个问题的洞察力。
发布于 2019-11-28 14:51:14
事实证明,简单地将一些有用的改进和一个新的改进结合在一起,就会产生很大的不同。
doEvents
(而不仅仅是当状态栏更新时-比如在Application.Calculate
调用之前和之后)。
而且,最重要的是
CTRL+Break
)比UserForm按钮和窗体关闭按钮反应更快,不会意外地结束excel任务。,这是对成品进行抛光的过程
首先,设置一个debugMode
或逆handleErrors
模块级变量,以控制是否实现中断到取消和错误处理。(错误处理将使您的代码更难调试,因此您将欣赏此开关)
如果您的进程正在处理错误,则将Application.EnableCancelKey
设置为xlErrorHandler
和On Error GoTo [ErrorHandlingLabel]
。错误处理标签应该在清理之前直接设置,并立即将EnableCancelKey
设置为xlDisabled
以避免错误。在继续执行清理步骤之前,您的处理程序应该检查存储的Err.Number
并采取相应的措施。
确保如果您遵从脚本中的任何其他复杂的vba (例如在带UDF的工作表上使用Application.Calculate
),则先设置On Error GoTo 0
,然后设置On Error GoTo [ErrorHandlingLabel]
,以避免捕获单元格绑定错误。
不幸的是,要使UX始终可读,就必须在xlDisabled
上保留cancel键,直到表单关闭。
和代码:
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
在UserForm中
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
If MyLong.handleErrors Then Application.EnableCancelKey = xlInterrupt
End Sub
请注意,这个方法可能会产生一些错误--您没有预料到,因为执行直接跳转到指定的标签。您的清理代码需要从一开始就实例化所需的变量。
总之,一旦解决了这些问题,此设置确保用户可以尽可能多地单击CTRL+Break
,而不会导致崩溃或弹出窗口。
https://stackoverflow.com/questions/59025389
复制