4

Alternate Title: Why does pressing Esc make my MS-Word macro run faster

While waiting for some code to run I stumbled across something interesting.

Code runs slow... press Esc code runs fast. Example:

  • press Esc immediately after execution - 2 sec to complete
  • don't press Esc at all - up to 30 sec to complete

To me this makes no sense. It turns out other people have noticed similar behavior, for example:

And they have found various solutions or workarounds. However, these posts are for MS-Excel; which it seems has different behavior for the Esc key.

In MS-Excel pressing the Esc key can (depending on the setting of Application.EnableCancleKey) interrupt code or raise an error (Err 18), or do nothing. On the other hand in MS Word no such thing happens, instead Application.EnableCancleKey alters the behavior of Ctrl + Pause. Yet despite this, pressing the Esc speeds up the code execution dramatically.

That difference aside, my question relates more to the placement of code in a userform. For example in my Userform:

Private Sub Cmd_Click()

    Module1.Macro1
    Module1.Macro2
    Module1.Macro3

End Sub

Tested on a 64 bit version of Word 2010, using the structure above is significantly slower than this:

Userform:

Private Sub Cmd_Click()

    Module1.RunMacro123

End Sub

Standard Module:

Private Sub RunMacro123()

    Module1.Macro1
    Module1.Macro2
    Module1.Macro3

End Sub

I should note the following:

  1. This is most apparent in the 64 bit version of MS Word, the 32 bit version seems to run quite fast with the original code (I'm yet to test with the revised code)
  2. Like the author in the first link I am not using the selection object etc.
  3. I'm more interested any insights as to why the speed of code execution is so influenced by:
    • pressing Esc
    • moving the calls from the userform to a standard module
  4. Macro1, Macro2 & Macro3 create and edit document styles and (FWIW) involve reading INI files many times

On a side note, in a hair-brained attempt at being clever I tried using sendKeys to send the Esc key but it has no affect.

Edit - Results of timing the code:

I ended up using the timer function to time the code, I had already implemented a stack class which was adapted from here: http://www.tek-tips.com/viewthread.cfm?qid=1468970

I added an 'absolute' timer (debug.print timer - startTime) to the call stack such that the time between each push was recorded and the timer was reset (startTime = timer) at each pop. Doing this made comparing times easier in NotePad++

This allowed me to determine that a sub that applies styles to a document was taking ~0.04 seconds to apply the style (NB value returned by the timer = seconds past midnight).

The image below shows an example of the code timing results. Basically, as far as I can tell, the delay in code execution comes from many incremental delays all associated with the same basic task. Comparing code execution times in NotePad++

Because of the way the timer works with the call stack I had to test the code getStyleElement to make sure it was not contributing significantly to the extra time. I did this by timing the code directly and was able to confirm that it was consistently fast to run.

Examining the rest of the code confirmed that the issue was with applyStyleFormat (which calls getStyleElement).

The styles are applied to the document - the code structure includes a With block and For Loop; something like this:

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

I'm no clearer as to why the code runs faster from outside of the userform, or after pressing Esc but it does and it seems to have something to do with modifying styles...

braX
  • 11,506
  • 5
  • 20
  • 33
SlowLearner
  • 3,086
  • 24
  • 54
  • I suggest that you put some `debug.print Now & " : place in code"` absolutely everywhere to understand where it lags exactly – Thomas G Jul 26 '17 at 12:06
  • @ThomasG I appreciate what you are saying, I could use `vba.Timer` to find the lag, and (painful as it is) I might go back and do that - but I already have a solution... I just don't understand why the solution works, nor why pressing the Esc key speeds up the code. – SlowLearner Jul 26 '17 at 12:15
  • I dont understand either and it's precisely to try to understand it that you should know on which instruction(s) it lags. Timer wont be any help here – Thomas G Jul 26 '17 at 12:21
  • Point taken. Lemme revert back to my old code and not use Timer... um BTW why won't timer help (Returns a Single representing the number of seconds elapsed since midnight. SyntaxTimerRemarks In Microsoft Windows the Timer function returns fractional portions of a second)? – SlowLearner Jul 26 '17 at 12:38
  • 1
    using Timer here will be painfull as you need more instructions for each print. Doing a `Debug.print Now [place of code]` is much simpler and sufficient. its done in a couple of seconds – Thomas G Jul 26 '17 at 12:49
  • Maybe the message loop is getting too heavy or is locked by another thread. Try to add some `Application.DoEvents` to see if it changes anything. – Florent B. Jul 26 '17 at 12:53
  • Just out of curiosity, are you running on mac? – M-- Jul 26 '17 at 13:25
  • 1
    No, Win10 Word 64 – SlowLearner Jul 26 '17 at 13:27
  • This question is intriguing as it is yet to be resolved. Allow me to direct you to a performance profiler class from another SO Q&A https://stackoverflow.com/questions/31383177/vba-queryperformancecounter-not-working#answer-31387007 . So profile your code and then post the offending lines of code (you haven't posted any genuine code as far as I can see). The above timer has resolution of milliseconds far far superior to intrinsic VBA profiling. – S Meaden Aug 05 '17 at 10:16
  • @SMeaden thanks for that... I've not forgotten this question, just distracted. I will time the code and post back :) – SlowLearner Aug 05 '17 at 10:47
  • @ThomasG using timer was not so painful ;) pls see updates :) – SlowLearner Aug 06 '17 at 16:01
  • @FlorentB. there are some doEvents in the code already, also the new results from the timer suggest a fairly consistent delay in code execution... this is the result of many incremental delays - thanks for your thoughts :) – SlowLearner Aug 06 '17 at 16:02
  • @SMeaden thanks for the link. I ended up modifying a stack class that I had previously implemented - somehow that seemed easier; but I will check out the performance class as well, cheers. – SlowLearner Aug 06 '17 at 16:03

1 Answers1

0

Just pulling the thread on @Florent Bs comment, have you tried seeing what you can disable prior to running the macros in the click event? Things like

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

Just to see if things are any quicker? There might be other commands to cancel other things that people can add.

Terry Field
  • 105
  • 9
  • 1
    thanks for your thoughts - in MS Word there is (to the best of my knowledge) no equivalent to Application.EnableEvents & Application.Calculation. As far as screen updating goes, that is already disabled and furthermore screen updates are prevented via API: Private Declare PtrSafe Function LockWindowUpdate Lib "user32" (ByVal hWndLock As LongPtr) As LongPtr Appreciate pulling this thread could expose the issue BUT what I'm most interested in is the 'why'... I'll update the question a bit more in light of the recent test results already posted... Cheers, – SlowLearner Aug 07 '17 at 23:07