I'm working on a form in a VB6 application. I have a question if anyone can help.
if i create a function like this...
Public Function CheckUser(userID as integer) as boolean
CheckUser = False
'do stuff here
CheckUser = True
Exit Function
I'm calling this function from another procedure (same form) - can i use the Checkuser boolean value throughout the form, or is it only for this procedure? Basically I'm trying to return a boolean value back to my procedure after running this function - and don't really want to declare a new modular variable and go that way. Trying to keep it neat.
yes you can if you declare CheckUser as a global, otherwise it is not possible because of local in method..
this is will help you how to declare global variable.
how to declare global variable
This is a question of scope:
https://support.microsoft.com/en-ca/kb/141693
The general rule is that a variable is only in scope for descendant members of the structure in which it was declared. If you declare a variable in a function, as is happening here through VB's implicit function variable, that variable only exists within that function.
The fact that the access modifier is public has nothing to do with it. You can read more about access modifiers here:
What is the difference between Dim, Global, Public, and Private as Modular Field Access Modifiers?
Firstly. VB6 is a forms package and application object (plus a printer and clipboard object) that hosts the VBA language.
In programming there are functions (and methods and properties of objects are also functions) and variables.
In your code to keep using it you would have to go If CheckUser("12345") then each time you use it.
But function calls are slow. Parameters have to be put on the stack, local variables created on the stack, the return address put on the stack, then a call to a memory location that may not be in the CPU caches.
Therefore if using something more than once store it in a variable.
Dim GetCurrentUser As Boolean
GetCurrentUser=CheckUser("12345")
By placing the Dim at the top of a module it is available to all procedures in that module. Or by Public GetCurrentUser As Boolean for all procedures to access.
Related
I'm not sure if this was asked before, but I couldn't find it. Suppose I have a procedure with a local variable inside it. Normally, that variable is destroyed after the function finishes running. But in some cases, I'd like for it to persist, like in this example:
Function myFunction()
Dim runCount As Integer
runCount = runCount +1
debug.print "This function is now running for the " & runCount & " time."
End Function
In this example, the code wouldn't work, because the runCount would be reset each time. Of course, the simplest solution would be to declare a global variable instead, but in some cases, I want to avoid that for the sake of simplicity, encapsulation or other reasons.
So, is there any way to have the local variable persist after the procedure has finished running?
Use the Static keyword to declare your local variable, instead of Dim, and the variable's content will outlive a call to the procedure it's declared in.
e.g. this will work as intended:
Function myFunction()
Static runCount As Integer
runCount = runCount + 1
debug.print "This function is now running for the " & runCount & " time."
End Function
Using Static locals is arguably preferable to declaring module-scope variables, when the variable only makes sense in a local scope or is only used in one procedure.
Note that module-scope does not equate global scope. This variable is accessible anywhere in the module it's declared in, but not outside of it:
Option Explicit
Private foo As Long
Use the Private (or Dim, but I prefer to keep Dim for declaring locals) or Public keyword to declare module-scope variables. The Global keyword is deprecated, and does exactly the same thing as Public.
As Kostas K. correctly points out, VBA also supports Static members.
See this signature:
Function myFunction()
Is implicitly a Public member. This would be explicit:
Public Function myFunction()
VBA supports adding the Static modifier at the procedure level, so you can do this:
Public Static Function myFunction()
And now you have a Public function where every local variable is implicitly Static. This is too much implicit, easily bug-prone stuff going on for my own personal taste, so I would avoid it. But it's probably good to know it's there if you need it.
Do not use Static (imo).
Use Private on module level instead of is more preferable.
But much more preferable will be to pass counter to function ByRef.
In my program I am trying to cut down on repetitive code by putting it into global functions.
In my current setup I am calling the function like:
Call [Global variable].Close
(global variable is the name of the class module).
This function is looks like;
Public Function Close()
DoCmd.Close
Cancel = True
End Function
Unfortunately this wont compile properly. What am I doing wrong?? (As an aside I also want to pass variables to and from this function and want to know how this would be done).
Thanks for your help.
If you're using a class module, you need an instance of that module before you can call its members. That requires either creating the class module as pre-declared (which involves exporting the code, editing in Notepad, and re-importing), or creating an instance with the New keyword:
Dim myClassInstance As MyClass
Set myClassInstance = New MyClass
myClassInstance.Close
But, depending on your use-case, you might better off with the function being available in a standard module - then you can just call it from anywhere, including from within a query.
I am trying to set up a global variable (as form) and set it = Form_MyForm
I have used the Form Load event to make the assignment and since it is a global variable I am expecting all my procedures can use this variable of mine without me retyping the assignment in different procedures
Problem is that sometimes it works while at others times it fails to recognise my variable, at which point I have to close my form and re-open it to refresh the assignment
I looked at the many Event for Access Forms but not sure what they are and how they can be helpful to my situation
Thanks for your help guys!
Define your global variable(s) in an Access module (not behind any form). This allows any object (form, report, macro etc.) to have access to them.
Initialize the variables inside a Public Function or Public Sub within the module and have the function/sub called by the database's opening form (i.e., main menu or switchboard).
Now, variables can be used in expressions, queries, VBA, and other areas.
**If the global variable needs to be redefined by various parameters, set that up in the function/sub and re-call it from a specific trigger event.
Had the same problem with user defined global variables, any time there is an error thrown or you switch to the development environment, you lose the set value in the variable.
You could try using a Session Variable, from the TempVar collection, which I find to be much more stable. It is stays set until you close the Database or you unset it.
MyFormName = me.Form.Name
'Load the data into Session variables
TempVars.Add "SessMyFormName", MyFormName
'Use the value elsewhere
SelectedForm = TempVars![SessMyFormName]
'Remove All Session Variables Set Earlier
TempVars.RemoveAll 'Destroy Session
I'm trying to set up a sub to be called upon and use the value of its result in the main sub. So far I've been using Function to carry over the value. However, I was wondering if there are any alternative ways of doing the same thing? I figured ByVal/ByRef is another way to do it by using a Sub instead of Function. My current codes are as follow:
Sub Main()
Dim i as Long
i = lr("A")
'some other calculations using i
End Sub
Function lr(Tar As String) As Long
Dim twb As Workbook
Set twb = ThisWorkbook
lr = ThisWorkbook.Sheets(1).Range(Tar & Rows.Count).End(xlUp).Row
End Function
My question is, How would I write this if I were to use a Sub instead of Function? Thanks!
So far I've been using Function to carry over the value.
Great, that's what functions are for! When you only need to return a single value, the best way is always going to be a function.
Things get fuzzier when you start needing to return two or more values. You could:
Use ByRef parameters and use them as "out" values.
This is "ok" for procedures (Sub), and confusing for functions - what determines which parameter gets to be the function's return value, and which parameters get to be passed ByRef? How does the calling code know whether or not to pass an initialized value to those ByRef parameters?
A naming convention can help here:
Public Sub Foo(ByVal foo1 As String, ByRef outBar1 As String, ByRef outBar2 As String)
An "out" prefix can tell the calling code that the parameter is an out value.
Scope the variables at a level that is accessible by both the caller and the callee.
This is a bad practice that can easily lead to spaghetti code. Avoid it - variables should have the smallest necessary scope possible, and be passed between methods/functions/procedures/modules, not just globally scoped and accessed by anyone at any given time!
Create a class to encapsulate all the values the function should return.
Definitely more object-oriented, results in much cleaner, readable code. The only downside is that VBA doesn't really encourage you to do this, and consistently doing that will result in a myriad of classes that you can't quite organize.
I just installed Visual Studio 2010 Service pack (proposed on Windows Update), and I can see a new feature on the "intellisense" that means when I write a Function or Sub in VB.NET it doesn't auto-complete parameters with ByRef or ByVal...
1) Is there anyway that I can configure this option back to how it was before?
2) If I don't specify ByX, which one is used by default? (it seems like it is always ByRef)
It seems that this post covers your question:
http://msmvps.com/blogs/carlosq/archive/2011/03/15/vs-2010-sp1-changing-quot-byval-quot-vb-net-code-editor-experience.aspx
So no, there is no way to get the old behaviour. From now on ByVal is the default (what it was before) and it won't get added automatically to the method parameters.
In my opinion this is a good decision since it's making VB.NET a bit more consistent with C# and avoids unnecessary "noises"(it's already verbose enough).
Old behaviour:
Private Sub test(ByVal test As String)
End Sub
New behaviour
Private Sub test(test As String)
End Sub
Tim covered what you asked directly, but something else to keep in mind is that any reference type variable, like a user defined class even if passed by value will allow you to make changes to that instances properties etc that stay. It won't however allow you to change the entire object. Which may be why it seemed to you to be defaulting to by reference
Public Sub (Something As WhateverClass)
Something = New WhateverClass 'will result in no changes when outside this method
Something.Property1 = "Test" 'will result in an updated property when outside this method
End Sub
From MSDN:
The value of a reference type is a pointer to the data elsewhere in memory.
This means that when you pass a reference type by value,
the procedure code has a pointer to the underlying element's data,
even though it cannot access the underlying element itself. For
example, if the element is an array variable, the procedure code does
not have access to the variable itself, but it can access the array
members.
Beware when transferring routines to VBA, where the default is ByRef (see, e.g., "The Default Method Of Passing Parameters" at the bottom of this page, by the great Chip Pearson).
That can be messy.