为什么 VBA 代码从标准模块调用时运行速度更快(而不是从用户窗体调用)?

4

替代标题:为什么按下Esc键会使我的MS-Word宏运行更快

在等待代码运行时,我发现了一些有趣的事情。

代码运行缓慢...按下Esc键代码运行得很快。例如:

  • 执行后立即按下Esc键-完成需要2秒钟
  • 根本不按Esc键-最多需要30秒钟才能完成

对我来说这毫无意义。事实证明其他人也注意到了类似的行为,例如:

他们发现了各种解决方案或解决方法。然而,这些帖子是针对MS-Excel的;似乎Esc键有不同的行为。

在MS-Excel中按下Esc可以(取决于Application.EnableCancleKey的设置)中断代码或引发错误(Err 18),或者什么也不做。另一方面,在MS Word中没有这样的事情发生,相反,Application.EnableCancleKey改变了Ctrl+Pause的行为。尽管如此,按下Esc可以显著加速代码执行。

除了这个差异,我的问题更多地涉及将代码放置在用户窗体中。例如,在我的用户窗体中:

Private Sub Cmd_Click()

    Module1.Macro1
    Module1.Macro2
    Module1.Macro3

End Sub

经测试,使用上述结构,在64位版本的Word 2010上运行速度显著较慢。相比之下,以下结构速度更快:

用户窗体:

Private Sub Cmd_Click()

    Module1.RunMacro123

End Sub

标准模块:

Private Sub RunMacro123()

    Module1.Macro1
    Module1.Macro2
    Module1.Macro3

End Sub

需要注意的是:

  1. 在MS Word 64位版本中,这一点最为明显,32位版本使用原始代码似乎运行得相当快(我还没有测试修订后的代码)。
  2. 和第一个链接中的作者一样,我没有使用选择对象等。
  3. 我更感兴趣的是关于代码执行速度为什么如此受以下因素影响的任何见解:
    • 按下Esc
    • 将调用从用户窗体移动到标准模块
  4. Macro1、Macro2和Macro3创建和编辑文档样式,并且(FWIW)涉及多次读取INI文件。

附注:在一个毛脑袋的尝试中,我试图使用sendKeys发送Esc键,但它没有任何作用。

编辑-代码计时结果:

最终,我使用timer函数来计时代码,我已经实现了一个堆栈类,该类是从这里改编而来:http://www.tek-tips.com/viewthread.cfm?qid=1468970

我在函数调用堆栈中添加了一个“绝对”计时器(debug.print timer - startTime),以便记录每次推送之间的时间并在每次弹出时重置计时器(startTime = timer)。这样做可以使比较时间更容易在NotePad++中进行。

这使我能够确定将样式应用于文档的子过程花费了大约0.04秒来应用样式(NB计时器返回的值=午夜过去的秒数)。

下面的图像显示了代码计时结果的示例。基本上,据我所知,代码执行的延迟来自许多与同一基本任务相关的增量延迟。

Comparing code execution times in NotePad++

由于计时器与函数调用堆栈的工作方式,我不得不测试代码getStyleElement以确保它没有对额外时间产生显著贡献。我通过直接计时代码来完成这个测试,并确认其运行速度一直很快。

检查剩余的代码证实问题出现在applyStyleFormat(调用getStyleElement)中。

样式被应用于文档——代码结构包括With块和For循环;类似于这样:

For i = 1 to Styles.Count
    With aDocument.Styles(i)
        .Font.??? = Something
        ' or .Paragraph.??? = Something
    End With
Next i

我仍不清楚为什么代码在用户窗体外部运行得更快,或者在按下Esc后运行得更快,但它确实有些与修改样式相关的东西……


1
在这里使用计时器会很痛苦,因为每次打印都需要更多的指令。做一个 Debug.print Now [代码位置] 更简单且足够了。它只需要几秒钟就能完成。 - Thomas G
1
不支持Win10 Word 64。 - SlowLearner
这个问题很有趣,因为它还没有得到解决。请允许我引导您到另一个SO Q&A的性能分析器类 https://stackoverflow.com/questions/31383177/vba-queryperformancecounter-not-working#answer-31387007 。所以,请对您的代码进行分析,然后发布有问题的代码行(据我所见,您还没有发布任何真正的代码)。上述计时器的分辨率是毫秒级别的,远远优于内置的VBA分析。 - S Meaden
@SMeaden 谢谢你...我没有忘记这个问题,只是有些分心。我会计时代码并回复 :) - SlowLearner
@SMeaden 感谢提供的链接。最终我修改了之前实现的一个栈类 - 这样做似乎更容易;但我也会去看看性能类,谢谢。 - SlowLearner
显示剩余8条评论
1个回答

0
只是在@Florent Bs评论中探讨一下,您是否尝试过在运行单击事件中的宏之前禁用哪些东西?诸如此类的东西。
Application.EnableEvents = False
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual

'code
Module1.Macro1
Module1.Macro2
Module1.Macro3

Application.EnableEvents = True
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic

只是想看看是否更快?可能还有其他命令可以取消人们添加的其他事情。


1
感谢您的想法 - 在 MS Word 中,据我所知,没有与 Application.EnableEvents 和 Application.Calculation 等效的功能。就屏幕更新而言,已经禁用了屏幕更新,并且还通过 API 防止了屏幕更新:Private Declare PtrSafe Function LockWindowUpdate Lib "user32" (ByVal hWndLock As LongPtr) As LongPtr。感谢您拉动这个线程可能会暴露问题,但我最感兴趣的是“为什么”...鉴于已经发布的最近的测试结果,我将更多地更新问题...干杯! - SlowLearner

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接