Difference between Calling a Sub and Application.Run - excel

In my business, we have a few teams that work on very simple macros. I'm trying to make them all readable to each other and in somewhat of a similar format, so new joiners can start working on the data.
I mention simple macros, because no one will be using Subs with arguments - most are derived from Macro Recorder anyway
Half of the teams use:
Sub button()
Call sub1()
Call sub2()
Call sub3()
Call sub4()
End Sub
And the other half use
Sub button()
Application.Run("sub1")
Application.Run("sub2")
Application.Run("sub3")
Application.Run("sub4")
End Sub
I understand that if your sub has no arguments, then Application.Run has a use - but being as there's barely any notation on it - is there a reason people still use Application.Run("")?
Can every use of it be beaten in speed and process by Call?

You can pass parameters through application.run as well. I use it when I am looping through macros. in your above example instead of having to write this:
Sub button()
Call sub1()
Call sub2()
Call sub3()
Call sub4()
End Sub
you could write this:
for i = 1 to 4
application.run("sub" & i)
next i
if the subs took in a str parameter you could do this:
for i = 1 to 4
application.run("sub" & i, strVariable)
next i

I use Application.Run if I’m trying to run a sub that is private in another module. If I have a some kind of template where I want to hide the macros from the users I will declare it as private so they can’t run/see the macro from there macros dialog box.
In module1 I have
Private Sub priv()
MsgBox “Private”
End Suv
In module2 the below will give you a Sub or Function not defined error.
Sub callPriv()
Call priv()
End Sub
But in module2 this will run and display the message box
Sub callPriv()
Application.Run “priv”
End Sub
It’s also useful to use Application.Run if you are calling a sub in your sheet or thisWorkbook modules.

I am posting some of this as an answer, because I cannot comment.
# Badja : You said ….” I understand that if your sub has no arguments, then Application.Run has a use – …” I am not sure if you are possibly implying that Application Run can have no arguments? - That is not the case. In principal , you can use arguments with Application.Run just as you can with Call. The syntax and general working of it can be a bit more difficult to understand than with Call. I agree with you , that documentation on Application.Run is very sparse.
#Hasib_Ibradzic : I was not aware that you can use the Call function with strings. ??
As far as I know, it is one of the advantages of Application.Run over using Call, that the macro name part of the argument is taken as a sting, so that you can build that string up with variables. So you are not limited to hard coding, as I think is the case with using Call
#Dude_Scott : In your example, I think it is advisable to suggest/ note that including the module code name could be advisable. Without this, you could experience problems if you had Sub routines with the same name in different modules.
In your example, this could be advisable to do
Sub callPriv()
Application.Run "Module1.priv"
End Sub
_._____________
Some other things that might be useful to Note:
If you have a macro in another module which is Public, then you can Call it from another module
For example. In my ( German ) Excel I have in my first normal module , code name Modul1 , ( In English Excel, I think that would typically be by default Module1 ), this
Public Sub PbicModule1() '
MsgBox "Pubic Modul1"
End Sub
And in my first worksheet class code module, code name Tabelle1 ( In English Excel I think that would typically be by default Sheet1 ) , I have this
Public Sub PbicTabelle1() '
MsgBox "Pubic Tabelle1"
End Sub
And in my ThisWorkbook class code , code name DieseArbeitsmappe ( In English Excel , I think that would typically be by default ThisWorkbook ), I have this
Public Sub PbicThisWorkbook() '
MsgBox "Pubic ThisWorkbook"
End Sub
The following macro will work when it is in any module
Private Sub CallMePubics() ' https://stackoverflow.com/questions/55266228/difference-between-calling-a-sub-and-application-run
Call Modul1.PbicModule1
Call Tabelle1.PbicTabelle1
Call DieseArbeitsmappe.PbicThisWorkbook
End Sub
That last macro would fail if the called macros were Private.
This following macro equivalent using Application.Run , would work regardless of whether the called macros were Public or Private
Private Sub AppRunMePubics()
Application.Run Macro:="Modul1.PbicModule1"
Application.Run Macro:="Tabelle1.PbicTabelle1"
Application.Run Macro:="DieseArbeitsmappe.PbicThisWorkbook"
End Sub
So , the conclusion from that is adding slightly to what Dude_Scott said:
Application.Run allows you to run, from anywhere, both Private subs and Public subs that are anywhere.
Call only allows you to run from anywhere, Public subs that are anywhere
Ref:
https://stackoverflow.com/questions/31439866/multiple-variable-arguments-to-application-ontime
http://excelmatters.com/2017/04/07/passing-arguments-byref-using-run/
Example Workbook with my coding: MainFile.xls : https://app.box.com/s/prqhroiqcb0qccewz5si0h5kslsw5i5h
http://www.tushar-mehta.com/publish_train/xl_vba_cases/1022_ByRef_Argument_with_the_Application_Run_method.shtml

Related

Possible to write Code from cells to VBA?

I was wondering how I would call something in VBA to write its code while running? So I mean if I had the text in A1 read:
sub Write()
Call OtherScript
End Sub
So again that is text inside the cell not in a VBA script. And then in a script while its running it Calls "A1" and the code that's in A1 gets run through VBA without having to actually put it in there.
This is not a real code obviously, I am really just trying to find out if this is possible. A friend that helps me learn to code and works me through a lot of VBA's said he does not know how that would work so Im posting it here to see if possible.
Please, try the following code. Before running it, write in a cell:
sub WriteSomething()
Call OtherScript
End Sub
You cannot create a function/sub named Write because this word is illegal, meaning something else in VBA.
and in the next cell (on the same row):
sub OtherScript()
MsgBox "Hello!"
End Sub
I used "K2". Use it too, or adapt the range from the code. You should also have a Module3 standard module. Please, update the module name with the one existing in your vbProject. Anyhow, the code can also create the module...
Copy the next code and run it:
Sub write_Run_Subs()
'It needs a reference to 'Microsoft Visual Basic For Applications Extensibility x.x'
Dim vbProj As VBProject, objMod As VBComponent, mdlName As String
Dim rngStr As Range, strSub1 As String, strSub2 As String
Set rngStr = Range("K2")
strSub1 = rngStr.value
strSub2 = rngStr.Offset(0, 1).value
mdlName = "Module3" 'of course, it have to exist in ThisWorkbook vbProject
Set vbProj = ThisWorkbook.VBProject
Set objMod = vbProj.VBComponents(mdlName)
objMod.CodeModule.AddFromString strSub1
objMod.CodeModule.AddFromString strSub2
Application.Run mdlName & ".WriteSomething"
End Sub
It is only a simple code without too much error handling, but it should work... If you run it twice, it will insert two such subs, if not preliminarily check their existence.
If adding the necessary reference looks complicated, please firstly run the following code, which will add it:
Sub addExtenssibilityReference()
'Add a reference to 'Microsoft Visual Basic for Applications Extensibility 5.3':
ThisWorkbook.VBProject.References.AddFromGuid _
GUID:="{0002E157-0000-0000-C000-000000000046}", _
Major:=5, Minor:=3
End Sub

How can I run different subs one by one?

I'm new in VBA
I wrote a code like this:
sub a1
some codes
end sub
sub a2
some codes
end sub
when I run my code the only code that be executed is sub a1!
how can I run other subs?
If you have 2 procedures
Sub FirstProcedure()
'Some code
End Sub
Sub SecondProcedure()
'Some code
End Sub
and you want to run both of them sequencially, you just need to call them
Sub RunBoth()
FirstProcedure
SecondProcedure
'Some more code
End Sub
If you now run RunBoth it will call the FirstProcedure and after that has finished it will call SecondProcedure
If you just want to call one of the procedures from the VBA editor, place your cursor in one of the procedures and press Run at the toolbar or F5 on the keyboard.
Alternative to #PEH 's approach via Application.Run
This approach demonstrate possible usage via Application.Run. Similar to OP, I confined myself to procedure calls without passing arguments here.
A) Numbered procedure calls
If you'd dispose of numbered procedures with identical name prefix (e.g. "Proc1", "Proc2"),
you may code as follows:
Sub ExampleCall()
runNumberedProcedures "Proc", 2
End Sub
Sub runNumberedProcedures(ByVal ProcName As String, ByVal ProcNumber As Long)
Dim i As Long
For i = 1 To ProcNumber
Application.Run "Proc" & i
Next i
End Sub
Undocumented Caveat
Note that Run needs an additional module prefix for procedure names ranging from A to XFD (e.g. in the case of Module.a1 or Module1.a2),
as apparently VBA tries to avoid internal conflicts with Excel column names. Therefore a numbered procedure starting with "XFE" or "Proc" needn't be prefixed expressly.
Side note: Personally I'd prefer more meaningful naming conventions than a1,a2 or Proc1,... apart from testings.
B) Procedure calls via list of procedure names
If you want to run any procedure names in a given order, you might call a sub and pass a procedure list as argument (here: "Proc1,Proc2,Module1.a1"):
Sub ExampleCall2()
'[1]run listed procedures
runListedProcedures "Proc1,Proc2,Module1.a1"
'do other stuff
'...
'[2]run procedure a2 later
Run "Module1.a2" ' note module prefix for proc names from A to XFD !
End Sub
Sub runListedProcedures(ByVal ProcList As String)
Const Delim As String = ","
Dim procedures As Variant
procedures = Split(ProcList, Delim)
Dim i As Long
For i = LBound(procedures) To UBound(procedures)
Application.Run procedures(i)
Next i
End Sub

Is there a way to tell if the Sub currently running is the parent or a called child

I have routines in VBA that can run standalone or can be called by another routine. When called as a subroutine (child) or as a primary routine (parent) there are operations that I may or may not want to execute. Is there a built-in function in VBA that I can call that will tell me if my executing code is a parent or child?
I have created global variables to test for parent/child but I would like to have a more elegant solution.
I think a good way would be to have a procedure for the action itself that has a switch (parameter) and a procedure that calls it.
Private Sub MyProcedure(Optional ByVal IsChild As Boolean = True) 'set default here
If IsChild Then
'child
Else
'parent
End If
End Sub
Now you can have a procedure to call it
Public Sub ParentCallMyProcedure()
MyProcedure IsChild:=False
End Sub
Public Sub ChildCallMyProcedure()
MyProcedure IsChild:=True
'which would be the same as
MyProcedure
End Sub
Eg if you want to call MyProcedure from a button then use
Public Sub Button1_Click()
MyProcedure IsChild:=False
End Sub
In all other procedures just use MyProcedure and IsChild is default True.
At least this is more elegant than a public/global variable.
In .Net getting info for a method, which called a method is called Reflection. It is quite straight-forward in C# - How can I find the method that called the current method?. VBA does not support it, but you could run around it, and log somewhere data about it - through a variable or logging to a worksheet or database.
There is a way to do it, if you stop the code in the middle of the called sub/function and call the Call Stack diaglog box. E.g., imagine the following seqeuence:
Sub TestMe()
Testme2
End Sub
Sub Testme2()
Stop
End Sub
If you run the code and press Ctrl+L once you are on the Stop you would get this:
If you only run TestMe2 and press Ctrl+L, you would get it correspondingly:
While the following is not really a solution it may work depending on your setup:
This is my Occam's Razor solution that I have used in the past.
Public ChildCount as Integer
Sub EveryProc()
ChildCount = ChildCount + 1
... rest of code...
ChildCount = ChildCount - 1
End Sub
This allows me to test how far I am into subroutines as ChildCount will be 1 for the parent and >1 for the children. I think the first time into the VBA, ChildCount will be zero so you need to increment and decrement the variable at the beginning and end of every sub.
I am spoiled in SAP...

Check Return value of private sub

In an Excel 2007 workbook I have three Excel modules, each containing one subroutine. The Driver sub (UpdateDataFromOracle) calls the subs UpdateResponse and UpdateSched. The code is working fine, but I'd like to check the "return code" of each of the called subs. I only want the Driver sub visible to the user, so I made the subs in Modules 1 and 2 Private.
Module 1 Private Sub UpdateResponse
Module 2 Private Sub UpdateSched
Module 3 Public Sub UpdateDataFromOracle
Here's code from the Driver sub
Sub UpdateDataFromOracle()
'DECLARE VARIABLES
Dim varSchedReturn as variant
'...
Call UpdateResponse
Call UpdateSched
'I Would like to insert the "return code" check here
End Sub
Here's code from the Called sub
Option Explicit
Private Sub UpdateResponse()
'DECLARE VARIABLES
'...
If Sheets(strTempSheet).UsedRange.Rows.Count > 10 Then
UpdateResponse = 0
Else UpdateResponse = 90
End If
End Sub
To call the Private subs I had to abandon the "Call" and use"
Application.Run "Module1.UpdateResponse"
But I can't figure out how to get a return code that way.
I also made UpdateResponse and UpdateSched Private Functions, but I still couldn't figure out how to get a return code back.
When I made UpdateResponse and UpdateSched Public Functions, I can use a statement at the end of the called subs like:
Else UpdateResponse = 90
The problem is that the called subroutines are visible to the user if I leave the functions Public.
My goal is to have only the Driver sub visible to the user, and be able to evaluate some sort of "Return Code" from the called subs in the Driver sub.
Thanks for looking at this.
I didn't fully read the question, but change them to Function
Private Function UpdateResponse() As Integer
'DECLARE VARIABLES
'...
If Sheets(strTempSheet).UsedRange.Rows.Count > 10 Then
UpdateResponse = 0
Else
UpdateResponse = 90
End If
End Function
Then:
Dim response ' As Variant or Integer
response = Application.Run("Module1.UpdateResponse")
Also, there are 2 better ways with Option Private Module or a public variable in Module1
3 Ways to Call a Private Sub from Another Module
The answer #DougGlancy gave worked well. It's listed as a comment to my original question, so I'm adding this Answer to indicate that his answer was correct.
One option for Windows Excel that I have used is to set values on the user's machine that can be retrieved in a later process.
https://msdn.microsoft.com/en-us/library/z46c489x(v=vs.110).aspx?cs-save-lang=1&cs-lang=vb#code-snippet-1
Alternatives could be to set some properties of your Excel workbook or in Excel you can create names and values associated with them. (part of named ranges).
Lastly, you can add and change values in the windows registry.

Excel macro - divide procedure into more subs

My procedure has become too large and won't run anymore. I've redesigned my code in my mind, but I need to get my current code up and running before I start editing. I've looked online and they say you need to split your procedure into subs and call them, but none really explain or show how you do this.
Thanks!
Sub Sub1()
' Code...
End Sub
Sub Sub2()
' Code...
End Sub
Sub Main()
Sub1
Sub2
End Sub
The first step is to take parts of code that are duplicated, and created one subroutine or function that you put that part into. Then you call the subroutine or function instead of duplicating the code everytime you need it.
Sub MacroName
Call ProcedureName1
Call ProcedureName2
etc...
End Sub
Sub ProcedureName1
'insert your vba code here
End Sub
The Call statement pulled each of the subs as long as I called the correct named sub.

Resources