Running at once multiple Subs from an unique VBA Sub - vba

I have three different Subs available in a VBA module and wanted to call those series of Subs from an unique Sub activated through a VBA button.
Below the code running:
Sub Updateworkbook()
Call Unprotectworkbook
Call CopyAndPaste
Call Protectworkbook
End Sub
After the first Sub Unprotectworkbook() is run the other Sub are not called and executed. Why this happens?
Below the Unprotectworkbook() Sub code for your reference
Sub Unprotectworkbook()
Dim myCount
Dim i
myCount = Application.Sheets.Count
Sheets(1).Select
For i = 1 To myCount
ActiveSheet.Unprotect "password"
If i = myCount Then
End
End If
ActiveSheet.Next.Select
Next i
End Sub

Modify your code as follows (change End to Exit Sub):
Sub Unprotectworkbook()
Dim myCount
Dim i
myCount = Application.Sheets.Count
Sheets(1).Select
For i = 1 To myCount
ActiveSheet.Unprotect "password"
If i = myCount Then
Exit Sub
End If
ActiveSheet.Next.Select
Next i
End Sub
or you can simply change it to the next one:
Sub Unprotectworkbook()
Dim sh
For Each sh In Sheets
sh.Unprotect "password"
Next
End Sub

It is very hard to answer your question without seeing the code in all three subs.
Some pointers though:
You don't need to select each sheet in order to modify it - just use Sheet(i).Unprotect "password" in the for loop instead.
Also, since you have a for loop you don't need to code when it should end, if you have defined the For i = 1 To myCount statement correctly. In other words, remove the If i = myCount Then End part.
You could define the For loop like the following: For i = 1 To Application.Sheets.Count to simplify your code, then you can remove the myCount variable.
You should always define your variables with a datatype in order to minimize errors, e.g use Dim i As Integer instead.
Always use Option Explicit at the top of each module, also to minimize confusion and errors caused by typos etc.
I strongly advise you to run through a couple of tutorials on VBA, there are lots around. The following is just the first one up when searching, I haven't tried it: Excel VBA Basic Tutorial 1

If this helps, I recommend making another set of 3 subs to test blank items first. Otherwise use one of the other answers above.
Sub msgTEST0() 'Call msgTEST0
Call msgTEST1
Call msgTEST2
Call msgTEST3
End Sub
Sub msgTEST1()
MsgBox "MSG1" & Space(10), vbQuestion
End Sub
Sub msgTEST2()
MsgBox "MSG2" & Space(10), vbQuestion
End Sub
Sub msgTEST3()
MsgBox "MSG3" & Space(10), vbQuestion
End Sub

Related

Make Individual Serial for Print documents in Word

Dear Readers
I am trying to make individual Serial numbers ( incrementing number) for some Forms in Microsoft Word, so we can track each one of them much simpler between people.
I used this link and it did work,
but it needs always running Macro and I couldn't figure out how to make it automatic with just a simple Ctrl+P shortcut, so I used this second link for that reason,
finally, it looked so great but there is a problem since the second link is a just "before Print" code, there is always one extra print at the end since the Printing process starts exactly after macro ended. any cancel print process code out there?
how can I overcome this one?
Codes under the document
Private Sub Document_Open()
Register_Event_Handler
End Sub
Codes under the Module
Dim X As New EventClassModule
Sub Register_Event_Handler()
Set X.App = Word.Application
End Sub
Codes under the class
Public WithEvents App As Word.Application
Private Sub App_DocumentBeforePrint(ByVal Doc As Document, Cancel As Boolean)
' Run code directly inside this Sub OR
MsgBox "Before Print"
' Call another Sub here, note, Sub and Module name can't match
Call FilePrint
' See https://www.freesoftwareservers.com/wiki/compile-error-expected-variable-or-procedure-not-module-macros-microsoft-office-29982732.html
End Sub
and finally Codes of FilePrint section as a Module
Sub FilePrint()
Dim i As Long, j As Long
With ActiveDocument
j = CLng(InputBox("How many copies to print?", "Print Copies"))
For i = 1 To j
With .CustomDocumentProperties("Counter")
.Value = .Value + 1
End With
.Fields.Update
ActiveDocument.PrintOut Copies:=1
Next
.Save
End With
End Sub

Calling a Sub or Function that sometimes doesn't exist

I'm trying to create a macro (in PERSONAL.XLSB) that every time a workbook is opened, it checks a condition and in if it's true (this means the workbook opened contains an specific Sub), it calls this Sub.
Option Explicit
Private WithEvents App As Application
Private Sub Workbook_Open()
Set App = Application
End Sub
Private Sub App_WorkbookOpen(ByVal Wb As Workbook)
If condition Then Call Specific_Sub
End Sub
It runs fine when I open a file that contains that Sub, however, if the Sub is not in the file, the compiler returns the error “Sub or Function not defined”, naturally.
I’m trying very hard to find a way to do this and deal with the error, but On error GoTo doesn’t work because the compiler error is before the run time, so it’s not executed.
I guess I have to do this in a different way but I can’t picture how to do it, any help or ideas?
Thanks a lot!
Thanks to the answers I've discovered that the best way is to use Application.Run. To keep the code as simple as possible, I just changed the last part to look like this:
Private Sub App_WorkbookOpen(ByVal Wb As Workbook)
On Error Resume Next
If condition Then
Application.Run ("'" & ActiveWorkbook.FullName & "'!" & "Specific_Sub")
End If
End Sub
Thank you all.
I cobbled this together from a few web sites. The key is that your sub routines name is in a variable and application. run uses the variable. This gets past the compiler error you are running into
Sub SubExists()
Dim ByModule As Object
Dim ByModuleName As String
Dim BySub As String
Dim ByLine As Long
'
'Module and sub names
ByModuleName = "Module1"
BySub = "Specific_Sub"
On Error Resume Next
Set ByModule = ActiveWorkbook.VBProject.vbComponents(ByModuleName).CodeModule
ByLine = ByModule.ProcStartLine(BySub, vbext_pk_Proc)
If Err.Number = 0 Then
Application.Run BySub
End If
End Sub
Private Sub App_WorkbookOpen(ByVal Wb As Workbook)
SubExists
End Sub

VBA macro that clicks all the buttons

I'm trying to make a macro that clicks all the buttons in any document by getting all the buttons names and evaluating a call to these functions.
Code:
Private Sub CommandButton1_Click()
MsgBox "b"
End Sub
Private Sub CommandButton2_Click()
MsgBox "a"
End Sub
Sub test_macro()
For Each S In Worksheets(1).OLEObjects
Evaluate ("Call " + S.Name + "_Click")
Next
End Sub
What can be the problem here? and is there maybe another way to do it?
See if this works.
For Each OLEObject In Worksheets(1).OLEObjects
If TypeName(OLEObject.Object) = "CommandButton" Then OLEObject.Object = True
Next
Here are some additional documentations on OLEObject.

Run Macros if Sheet Does Not Exist

I am trying to only run a set of macros if a sheet doesn't already exist. I have a macro that creates a sheet and combines data from two sheets into it, and another that formats the new sheet. Since it needs to run on workbook open, I can't have it recreating the sheet again and again. I have been trying the following, but it gives the error: "sub or Function not defined":
Private Sub Workbook_Open()
If SheetExist("MyNewSheet") Then
End Sub
Else
Combine
Format
End Sub
You aren't doing anything if the sheet exists, so change your test.
Private Sub Workbook_Open()
If Not SheetExist("MyNewSheet") Then
Combine
Format
End If
End Sub
Function SheetExist(sheetname As String) As Boolean
SheetExist = True ' replace this with code from link below
End Function
Use the answers here: Excel VBA If WorkSheet("wsName") Exists for examples of functions that determine whether the sheet exists.
Yea, the problem is "End Sub" should be "Exit Sub" You can also use the solution above/below.
Your fixed code would be:
Private Sub Workbook_Open()
If SheetExists("MyNewSheet") Then
Exit Sub
Else
Combine
Format
End If
End Sub
Also:
Public Function SheetExists(ByVal WorksheetName As String) As Boolean
On Error Resume Next
WorksheetExists = (Sheets(WorksheetName).Name <> "")
On Error GoTo 0
End Function

Nested Macros: Scope: how to exit all?

I current have 2 Macros:
The second macro is called within the first to perform a task. However I have logic within the second macro that states if my variable LastRow < 3 then exit the sub. This of course takes us immediately back into macro 1. What I desire here is to then exit immediately macro 1 as well. The way I have attempted to do this is by making LastRow public within both macros.. so when we exit back into macro 1, we have:
sub macro1()
application.run("macro2")
if LastRow < 3 then
exit sub
end sub
where macro 2()
sub macro1()
Static LastRow As Long
if LastRow < 3 then
exit sub
else do something
end if
end sub
I believe I may the issue may be that Static is not giving macro 1 access to variable LastRow.
whats the best way to proceed?
Regards!
You could use End statement in this way:
sub macro2()
Static LastRow As Long
if LastRow < 3 then
'...here is End
End
else
'do something
end if
end sub
However, End has some disadvantages you should be aware of. Let me cite them base on MSDN:
Terminates execution immediately. Never required by itself but may be
placed anywhere in a procedure to end code execution, close files
opened with the Open statement and to clear variables.
When executed, the End statement resets all module-level variables and
all static local variables in all modules. To preserve the value of
these variables, use the Stop statement instead. You can then resume
execution while preserving the value of those variables.
The End statement provides a way to force your program to halt. For
normal termination of a Visual Basic program, you should unload all
forms. Your program closes as soon as there are no other programs
holding references to objects created from your public class modules
and no code executing.
You could use a Function instead of a Sub and return a Boolean for example.
Function macro2() As Boolean
'returns false if the last row is 2 or less, true otherwise
LastRow As Long
if LastRow >= 3 then
macro2 = True
'do something
end if
End Function
Then in your first macro:
sub macro1()
if Not macro2 Then Exit Sub
end sub
Declare the variable in the first macro and pass it ByRef to the second macro.
Sub Mac1()
Dim lLastRow As Long
Mac2 lLastRow
If Not IsTooBig(lLastRow) Then
'do stuff
End If
End Sub
Sub Mac2(ByRef lLastRow As Long)
lLastRow = 5
If IsTooBig(lLastRow) Then
Exit Sub
End If
End Sub
Function IsTooBig(ByVal lLastRow As Long) As Boolean
IsTooBig = lLastRow >= 5
End Function
ByRef means that whatever changes you make to lLastRow in Mac2 will be reflected in lLastRow in Mac1.