Home » excel » vba – Automatically execute code before and after running any procedure

vba – Automatically execute code before and after running any procedure

Posted by: admin May 14, 2020 Leave a comment

Questions:

Before launching any heavy script I need to make some performance tweaks with Excel:

'Save parameters
screenUpdateState = Application.ScreenUpdating
statusBarState = Application.DisplayStatusBar
calcState = Application.Calculation
eventsState = Application.EnableEvents

'Turn them off
Application.ScreenUpdating = False
Application.DisplayStatusBar = False
Application.Calculation = xlCalculationManual
Application.EnableEvents = False

So I paste this code (or call special procedure, containing this code, no difference) before running almost every procedure. Is there a way to do this automatically (some kind of global constructor)?

And just the same situation with code after finishing the procedure:

'Put everything back
Application.ScreenUpdating = screenUpdateState
Application.DisplayStatusBar = statusBarState
Application.Calculation = calcState
Application.EnableEvents = eventsState
How to&Answers:

This example only work with 1 procedure of 1 module but you can iterate all the procedures of all modules and use the same logic. It uses the procedures from Jon Crowell.

Private Sub SwitchHeaderFooter()

    Dim lineNr As Long
    Dim procName As String
    Dim strHeader As String
    Dim strFooter As String

    procName = "TestProc"
    strHeader = "Call GetReadyToProcess"
    strFooter = "Call ReturnSettingsToWhatTheyWere"

    Dim vbComp As VBIDE.VBComponent
    Dim vbModule As VBIDE.CodeModule
    Set vbComp = ThisWorkbook.VBProject.VBComponents("ModuleTest")
    Set vbModule = vbComp.CodeModule

    lineNr = vbModule.ProcBodyLine(procName, vbext_pk_Proc)
    If (vbModule.Lines(lineNr + 1, 1) = strHeader) Then
        vbModule.DeleteLines lineNr + 1, 1
    Else
        vbModule.InsertLines lineNr + 1, strHeader
    End If

    lineNr = vbModule.ProcCountLines(procName, vbext_pk_Proc)
    If (vbModule.Lines(lineNr - 1, 1) = strFooter) Then
        vbModule.DeleteLines lineNr - 1, 1
    Else
        vbModule.InsertLines lineNr, strFooter
    End If

End Sub

In your ModuleTest, before the 1st execution:

Sub TestProc()
    MsgBox "This is a test procedure!"
End Sub

And after the 1st execution:

Sub TestProc()
Call GetReadyToProcess
    MsgBox "This is a test procedure!"
Call ReturnSettingsToWhatTheyWere
End Sub

Finally, after the 2nd execution:

Sub TestProc()
    MsgBox "This is a test procedure!"
End Sub

Answer:

Code that loops through ranges and selects various things is frequently behind the need for this kind of optimization, and can almost ALWAYS be avoided. If you want help optimizing a heavy script, please ask another question with the resource-intensive code.

Without seeing an example of one of your “heavy scripts”, the best thing to do is put your setup and restore code in subs and call them before and after you run your procedures.

Sub HeavyLifting()
    Call GetReadyToProcess

    ' code for sub...

    Call ReturnSettingsToWhatTheyWere
End Sub

Sub GetReadyToProcess()
    'Save parameters
    screenUpdateState = Application.ScreenUpdating
    statusBarState = Application.DisplayStatusBar
    calcState = Application.Calculation
    eventsState = Application.EnableEvents

    'Turn them off
    Application.ScreenUpdating = False
    Application.DisplayStatusBar = False
    Application.Calculation = xlCalculationManual
    Application.EnableEvents = False
End Sub

Sub ReturnSettingsToWhatTheyWere()
    'Put everything back
    Application.ScreenUpdating = screenUpdateState
    Application.DisplayStatusBar = statusBarState
    Application.Calculation = calcState
    Application.EnableEvents = eventsState
End Sub

Answer:

Will take a little bit of playing with, but something along these lines should work and you can set it how you want it

NOTE: You need access to the VBA Project Object Model

Sub a()
Dim l as VBIDE.VBComponent
Dim strng As String
strng = "Sub b()" & vbCrLf & "***your routine***" & vbCrLf & "End Sub"
Set l = ThisWorkbook.VBProject.VBComponents.Add(vbext_ct_StdModule)
l.CodeModule.AddFromString strng

End Sub

You could expand this more and turn it into a function where you pass the sub name you are wanting or pass a module name if you want it attaching to an existing module.

If someone could point out what you should declare l as, I left it blank, but if there is a way to set it correctly, then it would mean you get Intellisene on it. – Got this from Miguel’s answer, which is in a similar vein

Now, I am off to have some fun with this! 🙂