为啥从标准模块(而不是用户窗体)调用 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 - 在用户窗体上手动调用文本框退出事件?