What happens when i call a sub vba - vba

I'm using the Call Function to activate other Macro's, but I'm not entirly sure how VB reads this function.
(I'm using this with MS Excel 2010)
For Example:
Sub main macro ()
blabla
blabla
Call Edit
Call Save
Call HistoryLog
End Sub
This code has some If funcions, and at some point will get to "Call Edit"
Here the Macro "Sub Edit()"will be activated. But I need this code to edit data, save the data into a new file and make a history log in an other Excel sheet.
If VBA is finished with the "Sub Edit()" part how will the code continu?
Does it Calls the "Sub Save()" part?
Or do I realy require to type "Call Sub Save" at the end of the "Sub Edit()" part?

VBA will process all lines of your code in the order they are given, unless an error occurs in one of them. As it is, your code will just stop at the point of error.
Where to call Save() really depends on how you want your code to work. If you want to make sure that after every call to Edit() changes are saved, I'd call Save() within Edit(). If sometimes you want to call Edit() without calling Save() you should not put the call to Save() into Edit().

When you call a procedure from within another procedure, program control returns to the line just below the Call line. In your example, when the Edit procedure reaches an End Sub or Exit Sub line, program control will continue on the Call Save line (the line just below Call Edit).
The Call keyword is included in the language for backward compatitbility. You can call the Edit procedure like
Edit
without the Call keyword. If you like using Call, it doesn't harm anything though.

Related

How can I run several macros where the Macro name begins with a specific string of characters?

I've created several custom functions which I would like to Register. Currently, I have a different procedure for which I specify the registration for each function (there's no issue with that piece). However, the only way I know of to registering all these functions is by calling each Macro by name in another procedure like this:
Sub spRegisterFunctions()
Call spRegisterCUSTOMAfunction
Call spRegisterCUSTOMBfunction
Call spRegisterCUSTOMCfunction
Call spRegisterCUSTOMDfunction
End Sub
I'm actually looking for something more dynamic so that every time I create a new function, and it's corresponding "spRegister..." procedure, I don't have to remember to add the "Call" code to the "Sub spRegisterFunction()" procedure for that specific function.
Here's an example of what attempting to do:
Sub spRegisterFunctions()
Dim mc as Macro
For Each mc in VBProject("NameOfProject").Module("NameOfModule")
If Left(mc.Name,10)="spRegister" then
Call mc
End If
Next mc
End Sub
As you can see, I'm attempting to run any macro in a specific module who's name begins with "spRegister". Obviously the code above will not work, partly because some of those objects don't even exist.
Is there any way to do this?

RunCode macro action from 'hybrid' Access web database error

I have an Access web database
In this database, I have a web form with a button on it
In its on-click event macro, it calls RunMacro Macro1 (client-macro) which fires successfully
in Macro1, if I call RunCode Function Name: DoTest() it returns the error: "The function you entered can't be used in this expression... Error Number 2426 (or 2950 if I have an "=" symbol in front of my function)
This issue is easily reproduced by doing these steps:
Create a blank web database in MS Access
Add and save a table
Create a default form from that table and put a button on it
Create a new VBA module "Module1" and put the following function in it:
Public Function DoTest()
MsgBox "Test function runs smoothly"
End Function
Create a client-macro "Macro1" with RunCode: Function Name: DoTest() (Note that IntelliSense recognizes the macro, and it runs fine from here)
Create the form's button's on-click macro event RunMacro: Macro Name: Macro1
Click the form's button from form-view to receive the error:
The function you entered can't be used in this expression.
You may have used a DoEvents, LBound, UBound, Spc, or Tab function in an
expression.
You may have used an aggregate function, such as Count, in a design grid or in a calculated control or field.
Clicking "Ok" shows error number 2426 or 2950 depending on if you have an equals sign before your function name or not in the client-side macro's RunCode command.
I've tried all of the tips from this very similar question without any luck. Access seems to find the function fine, as replacing the function name with gibberish gives a very different error.
What the heck am I doing wrong?
In my actual web database which uses Access Services published to SharePoint 2010, I use an If IsClient() Then macro statement on the form's button's on-click event to ensure that the VBA is only running in the client mode, but that is not relevant to the error I'm receiving.
After some extra digging, I came across this post in another forum.
Here, the author simply explains that what I'm trying to do doesn't work (when you would think that it should).
Because it's on another forum, I'll spell out the workaround that also worked for me (credit: jakedrew, UtterAccess post, Jun 9 2011). Basically, you need to use a client-form in addition to a client-macro as intermediate steps to get from your web form to VBA. I made this effective for my current application by doing the following:
I re-saved my function as a sub instead:
Public Sub DoTest(ByVal intArg As Integer)
MsgBox "Test sub runs smoothly with argument = " & intArg
End Sub
Create a client-form (mine is named "frmVBA_Bridge") and create the following OnOpen event:
Private Sub Form_Open(Cancel As Integer)
' run your code using this command. Note that if you don't have an argument, you won't include the part of this line following the comma
Application.Run TempVars("SubName").Value, TempVars("Arg1").Value
' reset the "parameters"
TempVars("SubName").Value = ""
TempVars("Arg1").Value = 0
DoCmd.Close acForm, Me.Name, acSaveYes
End Sub
The client-side macro "Macro1" now instead does:
OpenForm
Form Name frmVBA_Bridge
View Form
Where Condition
Data Mode
Window Mode Hidden
The web form's button's on-click embedded macro gets changed to:
If IsClient() = True Then
SetTempVar
Name SubName
Expression ="DoTest"
SetTempVar
Name Arg1
Expression = 100
RunMacro
Macro Name Macro1
End If
Quite a bit of a workaround for such a simple thing, but it seems to do the job!

Procedure not executing all lines of code

I am currently having an issue with a VBA script. It jumps over some lines of code without any error message. The strange about it is, this behavior does not happen for all users that work with the VBA script.
The VBA script is part of an Excel workbook and is designed, to call a second procedure during the runtime. This second procedure should build a form and do some further actions (formatting, etc.). The VBA script enters the second procedure but does not execute one line out of it. Instead it just continues with the code from the main procedure.
Sub Main()
Dim oClient as Object
...
'Call procedure to create form
Call CreateForm(oClient)
...
End Sub
Sub CreateForm(ByRef oClient As Object)
Dim oForm As Object
Set oForm = oClient.VBProject.VBComponents.Add(3)
With oForm
.Properties("Caption") = "Partner Export"
.Properties("Name") = "frmPartner"
.Properties("Width") = 300
.Properties("Height") = 240
End With
...
End Sub
The code is reduced to the minimum to show the procedures. The real VBA code is way bigger and more complex- that is the reason why I split it into two procedures.
The object oClient is created in the main procedure and passed to the CreateForm procedure. After calling CreateForm, the VBA script just skips all lines of the sub without notification.
What could be the reason for this behavior? Especially with the background, that this does not appear for each user of it.
You confirmed my suspicions in the comments, and the solution is to "Trust programmatic access to VBA project object model" in the Macro Security settings. This allows VBA to refer to VBProject objects.

Which SUB is calling this SUB

Does anyone out there know how to do a stack trace in access-vba. I'm trying to do something like:
Public Sub a()
Call c
End Sub
Public Sub b()
Call c
End Sub
Public Sub c()
Debug.Print "Which sub has called me ?"
End Sub
What I want to do in Sub c is to show if that has been called by Sub a or Sub b without passing any arguments. In c I would simply show the stack but I have no idea if this is even possible in VBA - any thoughts ?
You can access the call stack during runtime under the menu View -> Call Stack
Alternatively you can use the keyboard shortcut CTRL+L during runtime.
You can use Mztools Addins which has an option to view the procedure caller.
Download Mztools
The only way you could tell would be to get the return address pointer of the calling subroutine from the stack. VBA wouldn't do that directly, you would have to have written your own library or have a library from someone else who needed to know these things. This might be worthwhile if the calling routines are from a program you can't update, however if this is only coming from your own code, it's far easier to rewrite your code to identify the caller.
The way I do this is by having a ProcedureEnter and ProcedureExit call at the beginning and end of each VBA routine I have. The ProcedureEnter routine has an argument for the Sub/Function name, which gets stored in a global call stack collection. ProcedureExit just pops the last entry off the stack.
So, to get the caller routine name, you would just get the item at the Stack Collection .count - 1
I personally use MZTools to set up a default VBA routine header/footer, so that I just type
Function fnname(arg as whatever) as boolean
End Function
and click the Add Error Handler button in MZTools and it adds the ProcedureEnter and ProcedureExit calls for me. That makes it a lot less cumbersome to add the stack trace code.

How to call another module without returning to the first one after completion?

This is probably the dumbest question I've ever asked here, but it's hard to find answers to things like this.
I have a program with a bunch of modules/subs that each calculate a different variable. They're pretty complex, so I like to keep them separate. Now I want an earlier module to skip to another module based on user input. I thought I could use the call (sub name) method for this, but then the program returns to where the call line was and continues on that module from where it left off.
Example:
Module 1:
Sub NewPracticeSub()
Call otherpracticesub
MsgBox ("We've gone back to this sub... :(")
End Sub
Module 2:
Sub otherpracticesub()
MsgBox ("We're in the other practice sub!")
End Sub
I don't want it to return to Module 1. What can I do to have it switch control to Module 2 without it then returning to complete Module 1 upon completion of Module 2?
I feel like I just used the most confusing language possible to explain all of this, but thank you for your help anyways!!
Edit: I know I used the words module and sub interchangeably, and I know they're different. I like to keep each sub (which are each very large in my program) in their own modules because it's easier to keep track of them, and easier to explain/demonstrate the application flow to other people.
I think all you're looking for is the command Exit Sub which will make the program leave the subroutine without continuing any further, But the way you usually want to do this is, rather than calling a Sub, rather call a Function that returns a boolean value.
So, for example:
Public Function MyFunc() as Boolean
....
If [good] MyFunc = True
Else MyFunc = False
End Function
Then you could do something along the lines of:
Sub MyCallingSub()
...
If MyFunc = True then Exit Sub
Else ...
End Sub
It just adds in A LOT more felxibility and ability to choose whether you want to continue further in your sub or not.
Hope that makes sense.
Other than using the ugly End statement which I will describe below (and strongly recommend you to avoid), I'm not aware of any way to circumvent the call stack. Even John's response necessarily returns to the calling procedure, and evaluates another statement to determine whether to proceed or end.
This may yield undesirable outcomes, which is why I hesitate to recommend it, in favor of properly structuring your code, loops, etc., with respect to the call stack.
In any case, here is how you can use the End statement within your child subroutines, without needing any sort of public/global variables. This still allows you the flexibility to decide when & where to invoke the End statement, so it need not always be invoked.
Sub NewPracticeSub()
Call otherpracticesub, True
MsgBox ("We've gone back to this sub... :(")
End Sub
Sub otherpracticesub(Optional endAll as Boolean=False)
MsgBox ("We're in the other practice sub!")
If endAll then End '## Only invoke End when True is passed to this subroutine
End Sub
Why I say this method should be avoided, via MSDN:
"Note The End statement stops code execution abruptly, without
invoking the Unload, QueryUnload, or Terminate event, or any other
Visual Basic code. Code you have placed in the Unload, QueryUnload,
and Terminate events of forms and class modules is not executed.
Objects created from class modules are destroyed, files opened using
the Open statement are closed, and memory used by your program is
freed. Object references held by other programs are invalidated.
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."
It will always return but that doesn't mean its a problem. I suggest you use Exit Sub as follows:
Sub NewPracticeSub()
Call otherpracticesub
**Exit Sub**
'Nothing more can execute here so its no longer a worry
End Sub
Module 2:
Sub otherpracticesub()
MsgBox ("We're in the other practice sub!")
End Sub