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

Posted

技术标签:

【中文标题】为啥从标准模块(而不是用户窗体)调用 VBA 代码时运行得更快?【英文标题】:Why does VBA code run faster when called from a standard module (instead of a userform)?为什么从标准模块(而不是用户窗体)调用 VBA 代码时运行得更快? 【发布时间】:2018-01-01 17:17:49 【问题描述】:

备用标题:为什么按 Esc 会使我的 MS-Word 宏运行得更快

在等待一些代码运行时,我偶然发现了一些有趣的东西。

代码运行缓慢...按 Esc 代码运行很快。示例:

执行后立即按 Esc - 2 秒 完成 根本不要按 Esc - 最多 30 秒 即可完成

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

Pausing VBA and re-running it causes faster execution

http://www.vbforums.com/showthread.php?490446-ESC-key-while-running-macros

https://www.excelforum.com/excel-programming-vba-macros/617300-vba-code-runs-slow-until-i-push-esc.html

他们已经找到了各种解决方案或变通方法。 然而,这些帖子适用于 MS-Excel; Esc 键似乎具有不同的行为。

In MS-Excel pressing the Esc key 可以(取决于 Application.EnableCancleKey 的设置)中断代码或引发错误(Err 18),或者什么也不做。另一方面,in MS Word no such thing happens,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

我应该注意以下几点:

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

附带说明,为了聪明,我尝试使用 sendKeys 发送 Esc 键,但它有没有影响。

编辑 - 代码计时结果:

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

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

这让我可以确定将样式应用于文档的子应用样式需要大约 0.04 秒(计时器返回的 NB 值 = 午夜过后的秒数)。

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

由于计时器与调用堆栈一起工作的方式,我不得不测试代码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 之后运行得更快,但确实如此,而且似乎与修改样式有关...

【问题讨论】:

我建议你在任何地方都放一些debug.print Now & " : place in code",以了解它到底滞后在哪里 @ThomasG 我很欣赏你所说的,我可以使用vba.Timer 来找到滞后,并且(虽然很痛苦)我可能会回去这样做 - 但我已经有了解决方案。 .. 我只是不明白为什么解决方案有效,也不明白为什么按 Esc 键会加快代码速度。 我也不明白,正是为了尝试理解它,您应该知道它滞后于哪些指令。计时器在这里没有任何帮助 在这里使用 Timer 会很痛苦,因为每次打印都需要更多说明。执行Debug.print Now [place of code] 更简单且足够。它在几秒钟内完成 不,Win10 Word 64 【参考方案1】:

只是在@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

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

【讨论】:

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

以上是关于为啥从标准模块(而不是用户窗体)调用 VBA 代码时运行得更快?的主要内容,如果未能解决你的问题,请参考以下文章

Excel VBA - 在用户窗体上手动调用文本框退出事件?

Excel-VBA - 列出任何给定工作簿的所有用户窗体的控件

WPF中Closing窗体时调用Hide()方法异常

从用户窗体 vba 加载的公共数组

单击父窗体时不会触发访问 VBA 子窗体事件

VBA从添加到工作簿中导入udf模块