I know..
Application.Run ("Realcount2")
will call a macro. Can you end a macro with something like
Application.Stop("Realcount2")
You might be able to do something like this, but I'm not sure how reliable it will be. It seems to work for me on some simple tests, though.
Declare a public variable like StopMacro
Public StopMacro as Boolean
Assign this macro to your Button/etc, which sets a public variable StopMacro.
Sub SetStopMacro()
StopMacro = True
End Sub
Here is an example loop structure. Use DoEvents and within the loop, each iteration, check the value of StopMacro and ExitSub if true.
Sub Macro1()
Dim r As Long
StopMacro = False
For r = 1 To 100000
DoEvents
If StopMacro = True Then Exit Sub
'''''''''''''''''''''''''''''''''''''
' '
' Your code inside the loop '
' '
'''''''''''''''''''''''''''''''''''''
Next
End Sub
Related
Inside a sub ("Sensitivities") I am calling another sub ("MVE_NIM_Subs.MVE_Main"), if this sub outputs a MsgBox I want to end the sub and to go to the "Jump" defined, instead of continuing executing the sub. ¿How can I do it?
Thanks
Public Sub Sensitivities()
Application.Run "MVE_NIM_Subs.MVE_Main"
........
Jump:
End Sub
You can trap your Msgbox with a public boolean variable. In the sub MVE_NIM_Subs.MVE_Main modify your code to set a public boolean variable to be true if the msgbox appears.
After this sub ends, execution will go back to executing code inside sub Sensitivities. Then just check the value of the public boolean variable. If it's true, then go to Jump.
Something like this:
Option Explicit
Public DidMsg As Boolean
Sub Sensitivities()
DidMsg = False
Application.Run "MVE_NIM_Subs.MVE_Main"
If DidMsg = True Then GoTo Jump
'rest of your code
'
'
'
'
Jump:
'rest of your code after point Jump
'
'
'
'
End Sub
Sub MVE_Main()
'your code whatever it is
'right after using the msgbox type:
DidMsg = True
End Sub
MsgBox is a modal element, so code execution is paused until the user deals with it. This leaves you with two options:
Don't display a MsgBox (if you only want to stop code when this happens I'm not sure what the point of it is anyway?)
Create a non-modal userform to emulate a MsgBox instead.
You have to options in my opinion:
Make MVE_NIM_Subs.MVE_Main a function that returns Bool. When you display message box in MVE_NIM_Subs.MVE_Main set return value to True. Then in calling sub you can write:
If returnValue Then GoTo Jump
Keep MVE_NIM_Subs.MVE_Main as sub and declare some global Bool variable, that can be used within two subs. In outer sub set it to False before calling MVE_NIM_Subs.MVE_Main and in MVE_NIM_Subs.MVE_Main set it to True whenever you show the message box. Then you can use it in outer sub to decide whether to jump or not, like in first option :)
When I start my Userform, I first chack for a value. If this value is not existing the userform should close.
Try1: UserForm_Inizialize
Public Sub UserForm_Initialize()
Call languagePack
'
'initialize the userform
'
End Sub
Try1: function to choose a languagepack
Private Sub languagePack()
Dim LanguageItems(45) As String
Dim Language_ID As Integer
Language_ID = Outlook.LanguageSettings.LanguageID(msoLanguageIDUI)
Call Language_AS.getLanguage(Language_ID, LanguageItems)
If Not LanguageItems(0) = "" Then
With Me
'--write the array items into the userform objects and vaues
End With
Else
MsgBox "It doesn't exist a Language-Pack for your language! Pleas change to english."
Unload Advanced_Search ' will not work
End If
End Sub
Try1 was to unload the userform in the function languagePack(), but didnt stop run and I get an error. So I tried another thing:
Try2: UserForm_Inizialize
Private close_userform As Boolean
Public Sub UserForm_Initialize()
Call languagePack
If close_userform = Flase Then
'
'initialize the userform
'
else
Unload Advanced_Search ' will not work
end if
End Sub
Try2: function to choose a languagepack
Private Sub languagePack()
Dim LanguageItems(45) As String
Dim Language_ID As Integer
Language_ID = Outlook.LanguageSettings.LanguageID(msoLanguageIDUI)
Call Language_AS.getLanguage(Language_ID, LanguageItems)
If Not LanguageItems(0) = "" Then
With Me
'
'--write the array items into the userform objects and vaues
'
End With
close_userform = False
Else
MsgBox "It doesn't exist a Language-Pack for your language! Pleas change to english."
close_userform = True
End If
End Sub
Whats wrong on Try2? The Boolean close_userform is global so both functions can read the value. But if it reachs the unload it happen nothing. After reaching the end sub I get an error.
The error is: Run-time error '91':
Objective variable or With block variable not set
Macro that starts the Userform
Sub start_Advanced_Search()
Advanced_Search.Show (vbModeless)
End Sub
Pleas help me. Thanks for every command an answer. Kind regards, Nico
Why not make the languagePack sub a function that returns a Boolean instead of writing the result to close_userform. Next use Unload Me instead Unload Advanced_Search
That’s the solution of my problem.
Thanks Tom for your help.
Description:
If there is no language pack for my Userform (I make the language packs), it will close the userform.
The function languagePack() returns a boolean (ture = language pack exists | false = no language pack exists).
This boolean is saved in hasLanguage. With the function getHasLanguage() I can get the value outside of the userform.
This function is used in the sub start_Advanced_Search. With the if function I check if there is a language pack, if not it will unload the userform.
Userform
Private hasLanguage As Boolean
Public Sub UserForm_Initialize()
hasLanguage = languagePack()
If hasLanguage Then
'
'set the defaults...
'
End If
End Sub
Public Function getHasLanguage()
getHasLanguage = hasLanguage
End Function
Private Function languagePack() As Boolean
'array to save the new language
Dim LanguageItems(49) As String
'this value will contain the LanguageID of Outlook
Dim language_ID As Integer
'get LanguageID of Outlook
language_ID = Outlook.LanguageSettings.LanguageID(msoLanguageIDUI)
'call a sub to get the language
Call Language_AS.getLanguage(language_ID, LanguageItems)
'there is a languagepack if the first element of "LanguageItems" is not ""
If Not LanguageItems(0) = "" Then
With Me
'
'set the language of the userform
'
End With
languagePack = True
Else
'there is no languagepack
MsgBox "It doesn't exist a Language-Pack for your language! Pleas change to english."
languagePack = False
End If
End Function
Modul
Sub start_Advanced_Search()
'start the userform
Advanced_Search.Show (vbModeless)
'use the get function
If Not Advanced_Search.getHasLanguage() Then
'unload if flase
Unload Advanced_Search
End If
End Sub
I've got a workbook which runs a macro to show the userform Open1 as it opens, using the (very basic) code:
Private Sub Workbook_Open()
Open1.Show
End Sub
This does its job fine - each time I open the workbook, the userform pops up and runs perfectly.
But, I want the userform to appear the first time the workbook is opened only. Is there a way to allow this to happen?
You could use a dummy module which gets deleted the first time you open the spreadsheet...
Something like:
If ModuleExists("DummyModule") Then
Open1.Show
DoCmd.DeleteObject acModule, "DummyModule"
End If
Function ModuleExists(strModuleName As String) As Boolean
Dim mdl As Object
For Each mdl In CurrentProject.AllModules
If mdl.Name = strModuleName Then
ModuleExists = True
Exit For
End If
Next
End Function
Update: as stated, DoCmd isn't used in excel vba. That will teach me to write code without testing it!
The following updated code will work, but in order to access the VB environment, excel needs to be trusted.
There is a setting in the Trust Center>Macro Settings that you can tick for this code to work under Developer Macro Settings
As such, this may not be the way to go as it opens up the possibility of security issues...
Sub RemoveModule()
If ModuleExists("DummyModule") Then
Open1.Show
Dim vbCom As Object: Set vbCom = Application.VBE.ActiveVBProject.VBComponents
vbCom.Remove VBComponent:=vbCom.Item("DummyModule")
End If
End Sub
Function ModuleExists(strModuleName As String) As Boolean
Dim mdl As Object
For Each mdl In Application.VBE.ActiveVBProject.VBComponents
If mdl.Name = strModuleName Then
ModuleExists = True
Exit For
End If
Next
End Function
Try this:
If Sheets("Hide").Cells(1,1) = "1" Then
Open1.Show
Sheets("Hide").Cells(1,1) = "0"
End if
You must create the sheet Hide, and give the cell A1 the value 1, in that case the form will be shown.
After you create the sheet, hide it with this
Sheets("Hide").Visible = xlVeryHidden
And show it with this
Sheets("Hide").Visible = True
Here's an alternative bit of code that will persist between saves and allow you to reset it. No need to create a hidden sheet.
Put this in a module (invoke the DisplayFormIfFirstTime from your Workbook_Open event handler....)
Option Explicit
Private Const cMoniker As String = "FormHasBeenDisplayed"
Private Sub DisplayFormIfFirstTime()
If HasBeenOpened = False Then DisplayForm
End Sub
Public Sub DisplayForm()
MsgBox "Ok, its not a form but a dialog box...", vbInformation
End Sub
Public Function HasBeenOpened() As Boolean
Dim oName As Name
On Error Resume Next
Set oName = Application.Names(cMoniker)
On Error GoTo 0
If Not oName Is Nothing Then
HasBeenOpened = True
Else
Call Application.Names.Add(cMoniker, True, False)
End If
End Function
'Call this to remove the flag...
Public Sub ResetOpenOnce()
On Error Resume Next
Application.Names(cMoniker).Delete
End Sub
Based on the idea supplied by PaulG, I have coded an upgrade that will check for the name and if not found run a function, add the name and save the workbook for a more seemless approach to this problem...
Placed in ThisWorkbook
Private Sub Workbook_Open()
Run "RunOnce"
End Sub
Placed in a module
Sub RunOnce()
Dim Flag As Boolean: Flag = False
For Each Item In Application.Names
If Item.Name = "FunctionHasRun" Then Flag = True
Next
If Flag = False Then
Call Application.Names.Add("FunctionHasRun", True, False)
Application.DisplayAlerts = False
ActiveWorkbook.Save
Application.DisplayAlerts = True
Call RunOnceFunction
End If
End Sub
Private Function RunOnceFunction()
Open1.Show
End Function
Sub ResetRunOnce()
For Each Item In Application.Names
If Item.Name = "FunctionHasRun" Then
Application.Names.Item("FunctionHasRun").Delete
Application.DisplayAlerts = False
ActiveWorkbook.Save
Application.DisplayAlerts = True
End If
Next
End Sub
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
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.