How to end one procedure (Sub, Function etc) from another in VBA? - vba

I want to end a main sub from another sub or function.
Here is an example code to illustrate what I need to do:
Sub main()
Call endMainSub
'do other stuff
End Sub
Sub endMainSub()
'here I need a code to break main Sub
End Sub
From endMainSub, I would like to terminate main sub before "do other stuff".

You should use a function for that.
Sub main()
If Not endMainSub Then
'do other stuff
End If
End Sub
Private Function endMainSub() As Boolean
Dim Fun As Boolean ' function return value
' break if A3 = "No" OR C3 > 10
Fun = (Cells(3, "A").Value = "No")
If Not Fun Then
Fun = (Cells(3, "C").Value > 10)
End If
endMainSub = Fun
End Function
Make the function Private if you aren't going to call it from another module. Remove the Private to make it Public by default.

End Finishes all code execution. Alternatively, if you need more control, have a global flag (a boolean variable), based on which execution will continue or not.

Related

How do I exit sub where function is used?

Was wondering if it was possible to exit sub using a function :
For example
Public Function test()
Exit Sub 'does not work
End Function
Sub MySub()
test() 'Here we should Exit MySub()
End Sub
But this Trigger an error. Is there a way to do this ?
NOTE I don't want to exit the function but I would like to exit the Sub or Stop the code execution directly into my function.
That is not possible. However, if your intent is to have test decide whether or not to abort MySub, then just return a boolean from test and act on it in MySub:
Sub MySub()
If test() Then
Exit Sub
End If
End Sub
From MSDN:
Exit Function Immediately exits the Function procedure in which it
appears.
You should use Exit Function and not Exit Sub inside test()
Update:
If you wish to exit the calling sub MySub, you should return some value from the test function. For Example:
Public Function test()
test = False
End Function
Sub MySub()
If test() = False Then
Exit Sub
End If
End Sub
Try this approach, please. Your function must return something in order to exit the sub. Your sub is not automatically exited if the function is. You must set a condition inside the sub to exit it in case of a specific function return:
Public Function test(a As Long, b As Long) As Boolean
If a + b > 10 Then
test = True
Else
Exit Function ' even if it is not necessary
End If
End Function
Sub MySub()
If Not test(5, 7) Then Exit Sub
'do whatever...
End Sub

How can I declare one variable with assigned value that can use for all module in vba?

My question is how my variable and assigned value can be use for all module, for example like:
this is declare global module:
sub global()
public A as integer
A=3
end sub
this is first module(for sheet1):
sub first()
if Sheet1.Cells(1, "A") > 100 then
Sheet3.Cells(A, "A") = Sheet1.Cells(1, "B").Value
A=A+1
eng sub
this is second module(for sheet2):
sub second()
if Sheet2.Cells(1, "A") > 100 then
Sheet3.Cells(A, "A") = Sheet2.Cells(1, "B").Value
A=A+1
end sub
*i had separate the modules file for every single sheet such as sheet1 with module1 and sheet2 with module2.
so I expect that when A sum inside the first or second module will return to global() A value.
example of my contain file
Based on comments to Vityata's answer, and edits to the Question:
Module1
Option Explicit
Public A as Long
'Long and Integer use the same amount of Memory in VBA,
' so there is no reason not to use Long
Sub getglobal()
A=3
End Sub
Module2
Option Explicit
Sub first()
If Sheet1.Cells(1, "A") > 100 Then
Sheet3.Cells(A, "A") = Sheet1.Cells(1, "B").Value
End If
Module1.A=Module1.A+1
End Sub
Module3
Option Explicit
Sub second()
If Sheet2.Cells(1, "A") > 100 Then
Sheet3.Cells(A, "A") = Sheet2.Cells(1, "B").Value
End If
Module1.A=Module1.A+1
End Sub
What you need is achieved with declaring the variable like this Public A As Integer outside a sub or a function, in a module.
Public A As Integer
Sub SetGlobal()
A = 3
End Sub
And then somewhere else:
Sub first()
A = A + 3
MsgBox A
End Sub
In general, you cannot name a Sub Global, because it is a name, used by the VBE.
If you want to have a minumum value of the public variable, then you may consider creating a class, which has a condition in the Get-property. If the minimum value is 3, then the class should look like this:
Option Explicit
Private m_lA As Long
Public Property Get A() As Long
A = m_lA
If A < 3 Then A = 3
End Property
Public Property Let A(ByVal lNewValue As Long)
m_lA = lNewValue
End Property
And you can call it from the modules like this:
Option Explicit
Public myVar As New publicA
Public Sub TestMe()
Debug.Print myVar.A
myVar.A = myVar.A * 30
Debug.Print myVar.A
End Sub

How to exit a main sub within a called sub

I have 2 subs that look like this:
How can i stop my "main" sub if my statement is true in the sub i just called?
Public Sub main()
call run()
more code....
End Sub
Public Sub run()
If ProgressBar1.Value = 100 Then
(i want to stop the code running if this statement is true, it shall not continue in my main sub.)
End If
End Sub
You can't do it if Run() is a Sub. You need your Run() as a Function. return a Boolean indicating if you want to stop main, and call in inside an If:
Public Function run() As Boolean
If ProgressBar1.Value = 100 Then
Return True
End If
' some more code if needed
Return False
End Function
Then call it in your main sub like this:
Public Sub main()
' some main code here
If Run() Then
Return
End If
' The rest of main code here
End Sub

Excel vba nested inner subs like in python, java

In other programming languages there is a way to put some code into a separate function that gets called from the first one, for instance, in java:
public void exercise() {
run();
jump();
walk();
}
Is there a way in vba to put subs into one another? I tried to
Sub test1()
test2()
End Sub
Sub test2()
MsgBox("test2")
End Sub
But it gives mistake.
You would do this without parentheses:
Sub test1()
test2
End Sub
Parentheses are needed to enclose arguments in order to assign the output of a function to a variable. If your sub took arguments, you would do like:
Sub test1()
test2 "Hello", "World"
End Sub
Sub test2(arg1, arg2)
Msgbox arg1 & " - " & arg2
End Sub
You need to use call to call the subroutine:
call test2()

Use of delegates and invoke to call different sub depending on some variable

I would like to organize a class as follows (pseudocode)
Class MyClass
sub New(MethodEnabled as integer)
if MethodEnabled = 0
make MyMethod() to be Sub0()
else
make MyMethod() to be Sub1()
end if
end sub
sub MyMethod()
invoke(either sub0 or sub1 depending on "MethodEnabled" value)
end sub
sub Sub0()
some code here
end sub
sub Sub1()
some code here
end sub
End Class
What I wish is to have the sub "MyMethod()" to invoke (or "to be") either Sub0() or Sub1(), depending on how the class was constructed (either with MethodEnabled=0, or MethodEnabled=1).
I gather intuitively that I can do that by using delegates and invoke, but I am not much clear on how to actually do it, in practice.
Can anybody show me how I can possibly do this in the most elegant way. C# of VB.NET examples would be equally great. Thank you!
You need to either declare a delegate type or use System.Action, and then use an instance of that delegate type:
Class [MyClass]
Private Delegate Sub myDelegate()
Private myDelegateInstance As myDelegate
'or you could just leave out 'myDelegate' and use 'System.Action'
Sub New(ByVal MethodEnabled As Integer)
If MethodEnabled = 0 Then
myDelegateInstance = AddressOf Sub0
Else
myDelegateInstance = AddressOf Sub1
End If
End Sub
Sub MyMethod()
myDelegateInstance()
End Sub
Sub Sub0()
'some code here
End Sub
Sub Sub1()
'some code here
End Sub
End Class