Defining VBA Static Variable Causes Compiler Error - vba

According to Microsoft: https://msdn.microsoft.com/en-us/library/z2cty7t8(v=vs.100).aspx The following static variable declaration is correct.
Public Sub MyProc()
static count as integer = 0
count = count + 1
End Sub
According to Word 2010 VBA, this is a compiler error. It wants:
Public Sub MyProc2()
static count as integer
count = count + 1
End Sub
You have to assume that the static count is initialized to zero.
Hope this helps someone else.

The link in your question refers to VB.NET, not VBA. VBA requires the syntax as in your second example.
The correct link is Visual Basic for Applications Reference – Static Statement. There the syntax is described as follows:
Static varname[([subscripts])] [As [New] type] [, varname[([subscripts])] [As [New] type]] . . .
As you can see there, VBA doesn't allow assigning a value in the same statement the static variable is declared.

Thank you dee, Word VBA 2010 compiler likes this syntax. I prefer my static variables be defined instead of left to the whim of the compiler.
Static count As Integer: count = 1

Related

Circular Dependency Error when using Enumerations

I want to assign certain time weightage values to the variables of a data set. So I am trying to use Enumerations/Constants so to improve my code quality and make it easy to maintain. But on compiling my project, it throws in a Circular Dependencies Between Modules error.
What I have understood is, one is allowed to use only constants [Eg: 2,3.14,56....], in the truest sense of the word. Bottom line, if I can't use Either Enumerations or Constants. Then how can I achieve my objective of keeping my weightages code-block, in a way that any changes in them is reflected everywhere in my project than me having to do a find and update manually every instance.
What I am getting at, is to have a global variable that can be accessed throughout the project and is dynamic.
Private Const Wtage As Double = ConvTohr(34) 'Error happens here
Enum Weightage
Var1_Weightage = ConvTohr(3) 'Error happens here
Var2_Weightage = ConvTohr(11)
Var3_Weightage = ConvTohr(2)
var4_Weightage = ConvTohr(9)
var5_Weightage = ConvTohr(0)
End Enum
Private Function ConvTohr(val As Integer) As Double
If val = 0 Then
ConvTohr = 0
Exit Function
End If
ConvTohr = Round((val / 60), 2)
End Function
The error message is incorrect: your code does not have any circular references.
This is more of a bug in the VBA interpreter: your code is still incorrect and invalid, but VBA is showing the wrong error message.
Given that VBA remains effectively frozen-in-time since 2002 (excepting 64-bit support in 2007), so don't expect any fixes, let alone any enhancements, ever (Though MS Office's COM automation tooling is slowly shifting to JavaScript (maybe Python? please?).
The actual problem with your code is threefold:
You cannot use a Function to initialize a Const value in VBA.
You cannot use a Function to define values for a VBA Enum either.
You cannot have Enum members typed as Double: VBA only supports Integer and Long values for Enum members.
Curiously, VBA does allow Const values to be typed as Double - but most other languages don't because Double is an IEEE-754 floating point type that does not have a machine-portable representation, but as VBA is only for Win32 on x86/x64 I guess that means Microsoft made it work given the very narrow gamut of hardware that VBA programs will run on.
Anyway, if you want "named values" typed as Double that you can use anywhere, then try this:
Create a new Module (not a Class Module).
Rename the Module from Module1 to Weightage.
Put this code in Weightage:
Private Function ConvTohr(val As Integer) As Double
ConvTohr = Round((val / 60), 2)
End Function
Public Property Get Var1_Weightage() As Double
Var1_Weightage = ConvTohr(3)
End Property
Public Property Get Var2_Weightage() As Double
Var2_Weightage = ConvTohr(11)
End Property
Public Property Get Var3_Weightage() As Double
Var3_Weightage = ConvTohr(2)
End Property
Public Property Get Var4_Weightage() As Double
Var4_Weightage = ConvTohr(9)
End Property
Public Property Get Var5_Weightage() As Double
Var5_Weightage = ConvTohr(0)
End Property
Screenshot proof:
(See output in the Immediate pane):

VBA - Global Variable Scope?

I'm struggling here a bit.
I had created a few Public variables on a form (MainForm). I now realize I need to expand the scope of some of these variable to additional forms.
So, I moved the Public declarations to a new Module called "Globals".
When I attempt to run the new form, I am getting a complaint about an ambiguous variable as I attempt to reset its value to "". No complaint from the "MainForm" using the same variables.
Module "Globals"
Public myIP As String
Public myStatus As String
New Form:
myStatus = "" << throws an err
Am I correct to move the Public variable declarations to a Module?
Any thoughts about the "ambiguous variable" err from that new Form??
Thanks!
You may be trying to declare the same variable in the same scope. See here for more details, also a SO thread related to this here.
As a side note, I notice with Excel VBA you must appropriately qualify a module-level variable if a procedure-level variable is declared with the same name.

Is it possible to write a UDF in VBA that contains a period in the name?

Excel 2010 ships with some functions that contain a period in their name. For example STDEV.S and PERCENTILE.EXC
Is it possible to assign a name to my own function with a name such as PERCENTILE.CUSTOM using VBA?
For example, I would like to reference the following function using the formula =NAME.SUFFIX()
Option Explicit
Function NAMEdotSUFFIX() As Variant
NAMEdotSUFFIX = 42
End Function
As #SiddharthRout already suggested, external libraries have fewer restrictions than VBA when it comes to declaring function names, so periods are permitted in external names.
Despite the MSDN documentation, it is possible to use some special characters in VBA identifiers, including UDFs. The special characters can even be the first/only character in the identifier. The special characters will vary in their availability, according to the code-page of the VBA project and VBE.
This is a perfectly valid VBA function:
Public Function Foo·Bar()
Foo·Bar = 5
End Function
And can be used in an Excel formula:
=Foo·Bar()
5
Likewise, this gem of a function uses a non-breaking space as the function name:
Public Function  ()
  = 6
End Function
But while the function with an NBSP is valid, sadly, it can't be used in an Excel formula.
It is possible to use:
Public Function ·()
· = 5
End Function
BONUS HACK
Somebody forgot to tell the Enum team about the identifier rules. And while Enums can't be used in Excel formulas, you can use any character except a carriage return or square-brackets inside square brackets, as an enum name or member.
This is perfectly valid VBA:
Enum [Foo : Bar = 5 : End Enum]
[.B!a r"] = 6
End Enum
And the VBE pretty-printer turns it into this unparseable mess:
Enum Foo : Bar = 5 : End Enum
[.B!a r"] = 6
End Enum
It is not possible from VBA. This MSDN article shows how to define a function in VBA, and specifically this article lays out the rules for element names.

Is it possible to declare a public variable in vba and assign a default value?

I want to do this but it won't compile:
Public MyVariable as Integer = 123
What's the best way of achieving this?
.NET has spoiled us :)
Your declaration is not valid for VBA.
Only constants can be given a value upon application load. You declare them like so:
Public Const APOSTROPHE_KEYCODE = 222
Here's a sample declaration from one of my vba projects:
If you're looking for something where you declare a public variable and then want to initialize its value, you need to create a Workbook_Open sub and do your initialization there.
Example:
Private Sub Workbook_Open()
Dim iAnswer As Integer
InitializeListSheetDataColumns_S
HideAllMonths_S
If sheetSetupInfo.Range("D6").Value = "Enter Facility Name" Then
iAnswer = MsgBox("It appears you have not yet set up this workbook. Would you like to do so now?", vbYesNo)
If iAnswer = vbYes Then
sheetSetupInfo.Activate
sheetSetupInfo.Range("D6").Select
Exit Sub
End If
End If
Application.Calculation = xlCalculationAutomatic
sheetGeneralInfo.Activate
Load frmInfoSheet
frmInfoSheet.Show
End Sub
Make sure you declare the sub in the Workbook Object itself:
Just to offer you a different angle -
I find it's not a good idea to maintain public variables between function calls. Any variables you need to use should be stored in Subs and Functions and passed as parameters. Once the code is done running, you shouldn't expect the VBA Project to maintain the values of any variables.
The reason for this is that there is just a huge slew of things that can inadvertently reset the VBA Project while using the workbook. When this happens, any public variables get reset to 0.
If you need a value to be stored outside of your subs and functions, I highly recommend using a hidden worksheet with named ranges for any information that needs to persist.
Sure you know, but if its a constant then const MyVariable as Integer = 123 otherwise your out of luck; the variable must be assigned an initial value elsewhere.
You could:
public property get myIntegerThing() as integer
myIntegerThing= 123
end property
In a Class module then globally create it;
public cMyStuff as new MyStuffClass
So cMyStuff.myIntegerThing is available immediately.
Little-Known Fact: A named range can refer to a value instead of specific cells.
This could be leveraged to act like a "global variable", plus you can refer to the value from VBA and in a worksheet cell, and the assigned value will even persist after closing & re-opening the workbook!
To "declare" the name myVariable and assign it a value of 123:
ThisWorkbook.Names.Add "myVariable", 123
To retrieve the value (for example to display the value in a MsgBox):
MsgBox [myVariable]
Alternatively, you could refer to the name with a string: (identical result as square brackets)
MsgBox Evaluate("myVariable")
To use the value on a worksheet just use it's name in your formula as-is:
=myVariable
In fact, you could even store function expressions: (sort of like in JavaScript)
(Admittedly, I can't actually think of a situation where this would be beneficial - but I don't use them in JS either.)
ThisWorkbook.Names.Add "myDay", "=if(isodd(day(today())),""on day"",""off day"")"
Square brackets are just a shortcut for the Evaluate method. I've heard that using them is considered messy or "hacky", but I've had no issues and their use in Excel is supported by Microsoft.
There is probably also a way use the Range function to refer to these names, but I don't see any advantage so I didn't look very deeply into it.
More info:
Microsoft Office Dev Center: Names.Add method (Excel)
Microsoft Office Dev Center: Application.Evaluate method (Excel)
As told above, To declare global accessible variables you can do it outside functions preceded with the public keyword.
And, since the affectation is NOT PERMITTED outside the procedures, you can, for example, create a sub called InitGlobals that initializes your public variables, then you just call this subroutine at the beginning of your statements
Here is an example of it:
Public Coordinates(3) as Double
Public Heat as double
Public Weight as double
Sub InitGlobals()
Coordinates(1)=10.5
Coordinates(2)=22.54
Coordinates(3)=-100.5
Heat=25.5
Weight=70
End Sub
Sub MyWorkSGoesHere()
Call InitGlobals
'Now you can do your work using your global variables initialized as you wanted them to be.
End Sub
You can define the variable in General Declarations and then initialise it in the first event that fires in your environment.
Alternatively, you could create yourself a class with the relevant properties and initialise them in the Initialise method
This is what I do when I need Initialized Global Constants:
1. Add a module called Globals
2. Add Properties like this into the Globals module:
Property Get PSIStartRow() As Integer
PSIStartRow = Sheets("FOB Prices").Range("F1").Value
End Property
Property Get PSIStartCell() As String
PSIStartCell = "B" & PSIStartRow
End Property
there is one way to properly solve your question. i have the same concern with you for a long time. after searching and learning for a long time, finally i get a solution for this kind of question.
The solution is that no need to declare the variable and no need to set value to the variable, and even no need VBA code. Just need the "named range" in excel itself.
For example, the "A1" cell content is "hello, world". and we define the "A1" cell a name as "hello", that is, the "A1" cell have a name now, it's called "hello".
In VBA code, we just need use this method [hello], then we can get the "A1" value.
Sub test()
msgbox [hello]
end sub
the msgbox will show "Hello, word".
this way, we get a global variable without any declaration or assignment. it can be used in any Sub or Function.
we can define many named range in excel, and in VBA code we just use [] method to get the range value.
in fact, the [hello] is a abbreviation of the function Evaluate["Hell"], but it's more shorter.
It's been quite a while, but this may satisfy you :
Public MyVariable as Integer: MyVariable = 123
It's a bit ugly since you have to retype the variable name, but it's on one line.

I'm not sure what Option explicit means? [duplicate]

This question already has answers here:
Closed 12 years ago.
Possible Duplicate:
what’s an option strict and explicit?
Is it about case sensitivity? Complete noob here.
According to MSDN:
Used at file level to force explicit declaration of all variables in that file.
Otherwise, you can just use a variable without having to declare it first.
They even included an example:
Option Explicit On ' Force explicit variable declaration.
Dim MyVar ' Declare variable.
MyInt = 10 ' Undeclared variable generates error.
MyVar = 10 ' Declared variable does not generate error.
When option explicit is off visual basic allows you to implicitly declare a variable by assigning a value to it. This is a really bad idea as misspelling a variable name would silently create a new variable causing a very hard to find bug.
Option Explicit Off
Imports System
Public Class ImplicitVariable
Public Shared Sub Main()
a = 33
Console.WriteLine("a has value '{0}' and type {1}", a, a.GetType())
End Sub
End Class