What better for memory usage: define constants in subs or in module - vba

I understand that is some of VB basics, but can't figure out how to ask google:
How VBA manage constants? In some languages compiler replace them by values. But how that works in VBA? Will it matter if i declare them in sub/function, not in global space. Especially, if a sub/function is called many times in runtime.
I'm often declare function name and some other strings as constants in sub/function space - it's easier for me to read my code.
For example: SUB_NAME in Get_AppExcel_Ref() in code below, to use it for logging error events.
If the Get_AppExcel_Ref() will be called couple times while programm running - SUB_NAME will be allocated in memory once, on first run, or on every call? Or maybe there is some kind perfomance issue and better declare SUB_NAME as global.
Private Sub Get_AppExcel_Ref(ByRef appObject As Object)
Const SUB_NAME As String = "Get_AppExcel_Ref"
On Error GoTo ERR_NOT_OPENNED
Set appObject = GetObject(, "Excel.Application")
Exit Sub
ERR_NOT_OPENNED:
If Err.Number = 429 Then
Err.Clear
Set appObject = CreateObject("Excel.Application")
Else
Call LOG.printLog("open Excel", Err, SUB_NAME)
End If
End Sub
'LOG - user class type variable, printLog params: Description, Error, Source Name

Declaring your Const as global will make no sense, as it would display the same string, no matter where you would use it.
You could declare it as a global variable (for example to save the extra parameter to you logging routine) and assign the name of the routine, but you would have the name of you routine as a (constant) string in your code also (so same amount of memory used). And at the end, it will completely mess up your logic, because when calling a subroutine, the content will be overwritten and when after that call an error occurs, your log will show the wrong routine name. So don't go that path.
As Paul Ogilivie writes in his comments, think about a constant as a read-only variable - and don't waste any thoughts about the exaxt implementation (but I think it is save to assume that the string is put only once in memory). You have more than enough memory available for your VBA code, and string handling is so fast that you will never experience any runtime issues.
My credo: Use everyhing as it fits your needs as programmer best - and readability is an important aspect to this. Don't care too much about memory consumption or runtime speed - except if you really hit problems. And if you do, these are most likely caused by other things.

Firs of all, welcome to SO.
I think the VBA compiler does also replace constants with their values, as with other languages. So they are not the same as variables.
I don't think a constant is necessary here. I only tend to use them for parameters, not just to replace any string.
Your code would be just fine like this:
LOG.printLog "open Excel", Err, "GetAppExcelRef"

Related

Variable in a form won't keep its value after being used in the call to another form

I have a form with a variable in it called "VigilTable." This variable gets its value from the calling string OpenArgs property.
Among other things, I use this variable in the call string when opening other forms.
But it only works the first call.
MsgBox VigilTable before the call will always show "Spring2022" or whatever on the first call but always comes up blank on succeeding calls (and I get "invalid use of NULL" when the called form attempts to extract the value from OpenArgs). The variable is dimmed as String in the General section of the form's VBA code.
So what's happening here? And can I fix it?
Thanks.
Ok, so you delcared a variable at the form level (code module) for that given form.
and we assume that say on form load, you set this varible to the OpenArgs of the form on form load.
So, say like this:
Option Compare Database
Option Explicit
Public MyTest As String
Private Sub Form_Load()
MyTest = Me.OpenArgs
End Sub
Well, I can't say having a variable helps all that much, since any and all code in that form can use me.OpenArgs.
but, do keep in mind the following:
ONLY VBA code in the form can freely use that variable. It is NOT global to the applcation, but only code in the given form.
However, other VBA code outside of the form can in fact use this variable. But ONLY as long as the form is open.
So, in the forms code, you can go;
MsgBox MyTest
But, for VBA outside of the form, then you can get use of the value like this:
Msgbox forms!cityTest.MyTest
However, do keep in mind that any un-handled error will (and does) blow out all global and local variables. So, maybe you have a un-handled error.
Of course if you compile (and deploy) a compiled accDB->accDE, then any errors does NOT re-set these local and global variables.
but, for the most part, that "value" should persist ONLY as long as the form is open, and if you close that form, then of course the values and variables for that form will go out of scope (not exist).
Now, you could consider moving the variable declare to a standard code module, and then it would be really global in nature, but for the most part, such code is not recommended, since it hard to debug, and such code is not very modular, or even easy to maintain over time.
So, this suggests that some error in VBA code is occurring, and when that does occur, then all such variables are re-set (but, the noted exception is if you compile down to an accDE - and any and all variables will thus persist - and even persist their values when VBA errors are encountered.
For a string variable, a more robust solution not influenced by any error, should be writing/reading in/from Registry. You can use the, let as say, variable (the string from Registry) from any workbook/application able to read Registry.
Declare some Public constants on top of a standard module (in the declarations area):
Public Const MyApp As String = "ExcelVar"
Public Const Sett As String = "Settings"
Public Const VigilTable As String = "VT"
Then, save the variable value from any module/form:
SaveSetting MyApp, Sett, VigilTable , "Spring2022" 'Save the string in Regisgtry
It can be read in the next way:
Dim myVal as String
myVal = GetSetting(MyApp, Sett, VigilTable , "No value") 'read the Registry
If myVal = "No value" Then MsgBox "Nothing recorded in Registry, yet": Exit Sub
Debug.print myVal
Actually, this proved not to be the the answer at all.
It was suggested that I declare my variables as constants in the Standard module but I declared them as variables. It appeared at first to work, at least through one entire session, then it ceased to work and I don't know why.
If I declare as constants instead, will I still be able to change them at-will? That matters because I re-use them with different values at different times.
I didn't do constants but declaring VigilName in the Standard module and deleting all other declarations of it fixed both problems.
While I was at it I declared several other variables that are as generally used and deleted all other declarations of them as well so that at least they'll be consistently used throughout (probably save me some troubleshooting later.
Thanks to all!

What are the risks of declaring a variable in the middle of the code?

I usually see in almost all of VBA codes all variables are declared after e.g. Sub/Function name line
I know and I used variable declaration in the middle of some of my codes (Not inside a loop) and saw no problems.
I usually avoided that because I see most of VBA example codes have them declared right after the first line. I just want to know what are the risks from an expert/experienced VB programmer point of view.
There are no risks of declaring it in the middle.
The effect of declaring a variable in the middle is that it can only be used after that point and not before (which is scope).
The lifetime of the variable is different: the variable is created (allocated and initialized to its respective flavour of zero) when you enter the procedure, but you may not actually use it until you reach its scope (the point in the procedure where it's declared).
Declaring inside or outside a loop does not make a difference in VB6/A as they do not have block scope, unlike VB.NET.
So there is no performance difference between the two approaches (because all variables are created when you enter the procedure), but there is a difference in usage (you may not use a created variable before its declaration line). If you think that distinction is helpful in making sure you are not using a variable wrongly, declare your variables only where needed. Otherwise you are free to pick any of the two approaches, just apply it consistently (it's probably not a good idea to declare most of the variables in the beginning and then some in the middle).
Declare your variables, when you actually need them. When you have all declarations lumped at the top of the procedure, refactoring becomes much harder. And when you want to double check your declaration as you read your code (or, perhaps, someone else), searching it at the top may be again quite inconvenient, unless you procedure is short.
I would try to declare variables in a location that conveys useful information to the next programmer, over and above being functionally correct. This normally means: follow the scoping rules of the language.
By declaring all variables at the top you are making them available (in scope) for the entire procedure. That increases the work for a reader in the future, trying to understand how they will be used. Better to have them as local as possible.
I would not declare them in a loop since that actually would not have significance in VB6/VBA - but someone else might find confusing or misleading, or worst case it may cause subtle bugs.
Of course remember that this is not the only coding practice that we should be mindful of - if the procedure is so long that the location of the variable declarations is a big problem, that's a really good sign that the procedure should be broken up into smaller discrete logical blocks. The variable declarations would just be a symptom, not the main cause.
IMO there were many bad programming practices back in the 90s and earlier when VBA/VB6 were invented, but the industry has significantly learned & improved since then. So code from that era (or inspired by it) is often not a good example.
Declaring your variables up front, at the top of your sub/function makes it easy for others (and perhaps for you if you come by the code after, say a month) to read and understand what your code needs to calculate, and what placeholders/variables are required for the code to function.
You can of course declare variables anywhere (as long as you remember not to use a variable unless you have actually declared it first). That can work, and it has no effect whatsoever on the performance of your code (unless your logic includes an early Exit Sub or Exit Function. In this case, there will be a difference in performance depending on if your code does actually allocate memory for the variables or not).
It just isn't good practice to declare some variables at the top then do some work, then declare another set of variables mid-code. There are exceptions of course. When the variable you declared mid-code is for a temporary use, or something like that.
Sub CalculateAge()
Dim BirthYear As Integer
Dim CurrentYear As Integer
'Code to fetch current year
'Code to get BirthYear from user/or document
'Code to report result
End Sub
Compare that with the following:
Sub CalculateAge2()
Dim BirthYear As Integer
'Code to ask the user or fetch the birth year from the document
Dim CurrentYear As Integer
'Code to populate currentYear
'Code to do the calculation and report result
End Sub
In the first example, there is a clear separation from variables and logic. In the second, everything is mixed.
The first example is a lot easier to read and understand, especially if you use a good naming convention.
If you look at how classes are written or defined, you will see properties usually are first declared, then methods/logic below. This is the common practice used to write code.
PS: In other languages, you can declare and assign variables in the same line. in C# or VB.Net you could say something like:
int Age = CurrentYear - BirthYear; //C#
Dim Age As Integer = CurrentYear - BirthYear 'VB.Net
This is great if you use a lot of temporary variables, that you don't intend to declare ahead of time or maybe it would be more clear if declared mid-logic. But that's not possible in VBA. You need a separate line to declare a variable, and another to assign a value. You end up with a lot of Dim ___ As ___ statements. You might as well move the declaration part somewhere else to reduce distraction while reading the logic. Again, this works best if you use a good and consistent naming convention. If not, you end up in a worse situation like:
Dim w As Integer
Dim a As Integer
a = 42 'we don't know what this variable is for
'but we know its type from the previous line
Some_Lines_Of_code_And_Logic
' more code
' more code
w = 2 'we don't know what (w) is for, and we have to
'look up its declaration to get a hint
'which might be tedious

Declaring Variables Memory Leaks

I am wondering what would be the most correct way to deal with memory when using VBScript. Should declare all variables right before I use them? The beginning of the program? I understand global vs local, however in my script all variables are local. I know that memory leaks will never be a problem when writing in VBScript 99.9% of the time, but I am also curious as to the 'best' way to clear and release memory within a script. By 'best' I mean, the timing of clearing variables/objects (right after you are done using them vs the end of the script), etc.
An example:
Dim fso: Set fso = CreateObject("Scripting.FileSystemObject")
Dim arrList : Set arrList = CreateObject("System.Collections.ArrayList")
Dim objDict: Set objDic = CreateObject( "Scripting.Dictionary")
Dim objEmail : Set objEmail = CreateObject("CDO.Message")
Dim someArray(), x, y, z, item
It's best practice to declare all variables, but not for the reason you assume. VBScript is sufficiently good at cleaning up after itself, so memory leaks usually aren't an issue. Most of the time you don't even need to release objects (Set var = Nothing) because they're automatically destroyed when leaving the context.
The reason why you still want to declare your variables is that you want to use Option Explicit in your scripts (which enforces variable declarations), so that you can avoid problems due to mistyped or otherwise uninitialized variables. Without Option Explicit VBScript would automagically create missing variables and initialize them with an empty/zero value. Silly example:
Dim foo : foo = 3
Dim bar : bar = 1
Do
bar = bar + fo 'mistyped variable, initilized as empty/0
Loop Until bar > 10
WScript.Echo bar
Running the above would create an infinite loop. If you add Option Explicit the script will instead immediately terminate with a runtime error:
C:\path\to\your.vbs(5, 3) Microsoft VBScript runtime error: Variable is undefined: 'fo'
The VBScript garbage collector runs at the end of every line to clear implicit variables and at the end of every procedure (end sub, end function, and end property) to clear explicit variables. Objects are similar but have added constraints. It works similar to VBA's garbage collector. By contrast JScript waits until 30,000 objects have gone out of scope before running and freeing memory.
An implicit variable is an unnamed variable - msgbox LCase(UCase("String") has two implicit variables - the result of UCase("String") and that is passed to LCase(implicitVar1) which returns implicitVar2 which is passed to Msgbox. An Explict variable is declared either by DIM or just by using it as in A=5 which creates an explicit variable called A.
VBScript, on the other hand, has a much simpler stack-based garbage collector. Scavengers are added to a stack when they come into scope, removed when they go ou t of scope, and any time an object is discarded it is immediately freed.
https://blogs.msdn.microsoft.com/ericlippert/2003/09/17/how-do-the-script-garbage-collectors-work/
VBScript’s garbage collector is completely different. It runs at the end of every statement and procedure, and does not do a search of all memory. Rather, it keeps track of everything allocated in the statement or procedure; if anything has gone out of scope, it frees it immediately
https://blogs.msdn.microsoft.com/ericlippert/2004/12/22/t4-vbscript-and-the-terminator/
Also
https://blogs.msdn.microsoft.com/ericlippert/2004/04/28/when-are-you-required-to-set-objects-to-nothing/
https://blogs.msdn.microsoft.com/ericlippert/2004/03/01/syntax-semantics-micronesian-cults-and-novice-programmers/
The CPU is a stack based machine (and VBScript a stack based virtual machine). When the CPU calls a function the calling program puts the parameters on the stack and the return address, adjust the stack frame and does a jump. The callee function creates local variables on the stack and also places the return value on it. When it returns the stack pointer is adjusted back to where it was which automatically frees all the above.

When should an Excel VBA variable be killed or set to Nothing?

I've been teaching myself Excel VBA over the last two years, and I have the idea that it is sometimes appropriate to dispose of variables at the end of a code segment. For example, I've seen it done in this bit adapted from Ron de Bruin's code for transferring Excel to HTML:
Function SaveContentToHTML (Rng as Range)
Dim FileForHTMLStorage As Object
Dim TextStreamOfHTML As Object
Dim TemporaryFileLocation As String
Dim TemporaryWorkbook As Workbook
...
TemporaryWorkbook.Close savechanges:=False
Kill TemporaryFileLocation
Set TextStreamOfHTML = Nothing
Set FileForHTMLStorage = Nothing
Set TemporaryWorkbook = Nothing
End Function
I've done some searching on this and found very little beyond how to do it, and in one forum post a statement that no local variables need to be cleared, since they cease to exist at End Sub. I'm guessing, based on the code above, that may not be true at End Function, or in other circumstances I haven't encountered.
So my question boils down to this:
Is there somewhere on the web that explains the when and why for variable cleanup, and I just have not found it?
And if not can someone here please explain...
When is variable cleanup for Excel VBA necessary and when it is not?
And more specifically... Are there specific variable uses (public variables?
Function-defined variables?) that remain loaded in memory for longer
than subs do, and therefor could cause trouble if I don't clean
up after myself?
VB6/VBA uses deterministic approach to destoying objects. Each object stores number of references to itself. When the number reaches zero, the object is destroyed.
Object variables are guaranteed to be cleaned (set to Nothing) when they go out of scope, this decrements the reference counters in their respective objects. No manual action required.
There are only two cases when you want an explicit cleanup:
When you want an object to be destroyed before its variable goes out of scope (e.g., your procedure is going to take long time to execute, and the object holds a resource, so you want to destroy the object as soon as possible to release the resource).
When you have a circular reference between two or more objects.
If objectA stores a references to objectB, and objectB stores a reference to objectA, the two objects will never get destroyed unless you brake the chain by explicitly setting objectA.ReferenceToB = Nothing or objectB.ReferenceToA = Nothing.
The code snippet you show is wrong. No manual cleanup is required. It is even harmful to do a manual cleanup, as it gives you a false sense of more correct code.
If you have a variable at a class level, it will be cleaned/destroyed when the class instance is destructed. You can destroy it earlier if you want (see item 1.).
If you have a variable at a module level, it will be cleaned/destroyed when your program exits (or, in case of VBA, when the VBA project is reset). You can destroy it earlier if you want (see item 1.).
Access level of a variable (public vs. private) does not affect its life time.
VBA uses a garbage collector which is implemented by reference counting.
There can be multiple references to a given object (for example, Dim aw = ActiveWorkbook creates a new reference to Active Workbook), so the garbage collector only cleans up an object when it is clear that there are no other references. Setting to Nothing is an explicit way of decrementing the reference count. The count is implicitly decremented when you exit scope.
Strictly speaking, in modern Excel versions (2010+) setting to Nothing isn't necessary, but there were issues with older versions of Excel (for which the workaround was to explicitly set)
I have at least one situation where the data is not automatically cleaned up, which would eventually lead to "Out of Memory" errors.
In a UserForm I had:
Public mainPicture As StdPicture
...
mainPicture = LoadPicture(PAGE_FILE)
When UserForm was destroyed (after Unload Me) the memory allocated for the data loaded in the mainPicture was not being de-allocated. I had to add an explicit
mainPicture = Nothing
in the terminate event.

Why do we declare variables at the start of a module?

As my first language and as completely taught from other's example I never questioned the standard practice in VBA of grouping all variable declarations at the start of the module, routine or function they are scoped to as in this example.
Sub Traditional()
Dim bVariable as Boolean
Dim OtherVariable
' Some code using OtherVariable goes here
'
' Now we use bVariable
bVariable = True
Do While bVariable
bVariable = SomeFunction()
Loop
End Sub
Now I'm learning that standard practice in other languages is to declare variables as close to where they are used as possible, like this:
Sub Defensive()
Dim OtherVariable as String
' Some code using OtherVariable goes here
'
' Now we use bVariable
Dim bVariable as Boolean
bVariable = True
Do While bVariable
bVariable = SomeFunction()
Loop
End Sub
This seems completely sensible to me as a defensive programming practice - in that it limits both span and live time (as explained in Code Complete), so I'm wondering if there is any reason for not doing the same in VBA? Possible reasons I can think of are memory, running time (e.g. repeatedly declaring inside a loop), tradition - arguably a good reason as there must be hundreds of thousands of VBA programmers who expect to see all used variables at the start of the routine. Are there any I've missed that might explain the benefit of this practice or at least where it came from?
I declare all my variables at the top. I think declaring them closer to first use masks (at least) two other problems that should be fixed first.
Procedures too long: If you're procedure is more than fits on a screen, perhaps it's doing too much and should be broken into smaller chunks. You'll also find that unit tests are way easier to write when your procedures are small and only do one thing.
Too many variables: If you have a bunch of related variables, consider using a custom class module or user-defined type. It will make the code more readable and easier to maintain.
If your procedures are short and you're using classes and UDTs, the benefits of declaring the variables at the point of use are lessened or eliminated.
I think both way are just different coding style in VBA
In old C Standard, all Declaration must be on the top, I think many people just adopt this habit and bring it into other PL such as VBA.
Declaring variable on the top is clear for short list of variable names. It will be unreadable for a long list of variable name
Declaring variable close to where it's being used is introduced later. I think this practice has a clear advantage over "declare on the top" for PLs that has optimizer or more scope than VBA. (Like you can declare variables where the scope is visible in a FOR loop only) Then the optimizer will change the scope for you. (In VBA words, it may change a GLOBAL variable to a PROCEDURE variable)
For VBA, no perference