Why is VBA saying that it has found an 'ambiguous name'? - vba

When compiling some code (declarations shown below) I get the error message 'Compile Error: Ambiguous name detected. SixTables'. I have looked here and elsewhere but cannot find anything that matches my problem. What appear to be the most common causes of this error, declaring two variables with identical names or giving the same name to a function and the sub that it is called from, do not apply. And yes, I know I could just change the name to something the system was happy with, but (1) I wouldn't learn what I'm doing wrong, and (2) I chose that name for a reason - it fits its purpose exactly :-)
Option Explicit
Dim ArmOfService As Byte
Dim CharacterNumber As Long
Dim CurrentTerm As Byte
Dim DecorationRollMade As Byte
Dim DecorationRollNeeded As Byte
Dim DiceSize As Byte
Dim GenAssignment
Dim GenAssignmentSwitchInt As Byte
Dim GenAssignmentSwitchOff As Byte
Dim iLoopControl
Dim jLoopControl
Dim kLoopControl
Dim LineIncrement As Integer
Dim lLoopControl
Dim Merc(100)
Dim NoOfDice As Byte
Dim OfficerPromotion(63 To 78) As Byte
Dim PromotionRollMade As Byte
Dim PromotionRollNeeded As Byte
Dim Roll As Byte
Dim SkillColumn
Dim SixTables
Dim SpecAssignmentSwitchEnd As Byte
Dim SurvivalRollMade As Byte
Dim SurvivalRollNeeded As Byte
Dim TechLevel As Byte
Dim Temp As Integer
Dim Term As Byte
Dim TestCount
Dim UnitAssignment
Dim WhichTable
Dim Year As Byte
EDIT: I'm so embarrassed that I can hardly bring myself to explain what the problem was. I knew I hadn't duplicated a name, since I was only using it once - as a function! Thanks all for your help, I'm now going to go and hide my face in shame...

From MSDN :
More than one object in the same scope may have elements with the
same name.
Module-level identifiers and project-level identifiers (module names
and referenced project names) may be reused in a procedure, although
it makes programs harder to maintain and debug. However, if you want
to refer to both items in the same procedure, the item having wider
scope must be qualified. For example, if MyID is declared at the
module level of MyModule , and then a procedure-levelvariable is
declared with the same name in the module, references to the
module-level variable must be appropriately qualified:
Dim MyID As String
Sub MySub
MyModule.MyID = "This is module-level variable"
Dim MyID As String
MyID = "This is the procedure-level variable"
Debug.Print MyID
Debug.Print MyModule.MyID
End Sub
An identifier declared at module-level conflicts with a procedure name.
For example, this error occurs if the variable MyID is declared at
module level, and then a procedure is defined with the same name:
Public MyID
Sub MyID
. . .
End Sub

Having had this issue many times, and not fully understanding why, I think there is an important fact that I have tested, and someone can confirm. It may be obvious to professional programmers, but I place this here for those that seek these answers on discussions, who are usually not professional programmers.
A variable declared within a sub() is not "declared" (memory assigned) until that sub() is executed. And when the execution of that sub() is complete, the memory is released. Until now, I thought Public variables acted in similar way; available to any module that used it, --BUT only existing as long as the module where they were declared was still executing.
However, for any Public variable who's declaration line is in any module in the workbook, that variable is declared and available at the execution of any module within the workbook, even if the module where the Public declaration exists is never called or executed.
Therefore, if you have a Public variable declaration in one module, and then again in a separate module that you plan to run independent of the first, Excel still sees 2 declarations for the same variable, and thus it is ambiguous.
To prove it, you can create a blank module within a workbook project, and add absolutely nothing except two Public declaration lines,
Public VariableX as String
Public VariableY as Integer
and those variables will be declared and available throughout the project, for any sub() executed. This fact is also probably the reason for the tip about minimizing the use of public variables.
Again, this may have been kindergarten level information for professional programmers, but most on this are not pros, and have to learn these lessons the hard way, or through discussion boards like this. I hope this helps someone.

Related

How to use Public variable to stop execution of VBA code

This VBA code was written to break password of any VBA Project. It is explained here that Public variables retain their values even after execution of VBA module. Is it possible that following VBA code (stored at Module Level or Class Level) be modified in such a way to prevent execution of (aforesaid VBA code) breaking of Project password? :-
Public Dim HookBytes(0 To 5) As Byte
Dim OriginBytes(0 To 5) As Byte
Dim pFunc As LongPtr
Dim Flag As Boolean
ReDim HookBytes(0) As Byte
ReDim OriginBytes(0) As Byte
pFunc = 9223372036854775807 ' https://msdn.microsoft.com/en-us/vba/language-reference-vba/articles/longptr-data-type
Flag = True
The 'Password breaker' you provided contains the same declarations as the ones you are wanting to store as public variables.
You are basically wanting to "Hi-Jack" these variables before the code has begun.
The only problem is that once the routine was to run, the publicly declared variables will now become procedural variables within the scope of the routine itself as soon as they are declared within Sub. The public variables will essentially be ignored as soon as the Dim statement within the procedure declares the same variable name.
Essentially, the method you are wanting to use to stop the password breaker will not work unfortunately.

Why should I use the DIM statement in VBA or Excel?

So there is a question on what DIM is, but I can't find why I want to use it.
As far as I can tell, I see no difference between these three sets of code:
'Example 1
myVal = 2
'Example 2
DIM myVal as Integer
myVal = 2
'Example 3
DIM myVal = 2
If I omit DIM the code still runs, and after 2 or 3 nested loops I see no difference in the output when they are omitted. Having come from Python, I like to keep my code clean*.
So why should I need to declare variables with DIM? Apart from stylistic concerns, is there a technical reason to use DIM?
* also I'm lazy and out of the habit of declaring variables.
Any variable used without declaration is of type Variant. While variants can be useful in some circumstances, they should be avoided when not required, because they:
Are slower
Use more memory
Are more error prone, either through miss spelling or through assigning a value of the wrong data type
Using Dim makes the intentions of your code explicit and prevents common mistakes like a typo actually declaring a new variable. If you use Option Explicit On with your code (which I thoroughly recommend) Dim becomes mandatory.
Here's an example of failing to use Dim causing a (potentially bad) problem:
myVar = 100
' later on...
myVal = 10 'accidentally declare new variable instead of assign to myVar
Debug.Print myVar 'prints 100 when you were expecting 10
Whereas this code will save you from that mistake:
Option Explicit
Dim myVar as Integer
myVar = 100
' later on...
myVal = 10 ' error: Option Explicit means you *must* use Dim
More about Dim and Option Explicit here: http://msdn.microsoft.com/en-us/library/y9341s4f.aspx
Moderators, I'm making an effort, assuming you'll treat me with due respect in thefuture.
All local variables are stored on the stack as with all languages (and most parameters to functions). When a sub exits the stack is returned to how it was before the sub executed. So all memory is freed. Strings and objects are stored elsewhere in a object manager or string manager and the stack contains a pointer but vb looks after freeing it. Seting a vbstring (a bstr) to zero length frees all but two bytes. That's why we try to avoid global variables.
In scripting type programs, typeless programming has many advantages. Programs are short and use few variables so memory and speed don't matter - it will be fast enough. As programs get more complex it does matter. VB was designed for typeless programming as well as typed programming. For most excel macros, typeless programming is fine and is more readable. Vbscript only supports typeless programming (and you can paste it into vba/vb6).

Function calls another function but gets "stuck"

I'm an occasional VBA programmer, for fun (not my job).
I have a series of VBA modules in MS Excel 2010. Can't figure out what I did wrong. This routine worked, then I changed some things and it stopped working. One of the things I did was split the code from a single function to two functions in two modules. I think it worked for a while after I split it into two, but now I can't remember if that is true since I've tried so many things to make it work again. Luckily, I saved the older version with all of the code in a single function, and it still works. It returns a large array to the spreadsheet.
Fundamentally, I have a worksheet that calls a function. That function calls another function. Using the Debug - Toggle Breakpoints in combination with some MsgBox calls, I have figured out that the first function runs down to the point that it calls the second function. Then the second function runs down to the "End Function" command. At that point, the name at the top of the worksheet flickers a few times...and nothing. While debugging, the program does not seem to return to the first function. The array that is supposed to be populated in the first function is filled with #Value.
I read several places where VBA can be corrupted, so shutting everything down and rebooting can fix it. It didn't. Then I read that if you copy/paste everything into a new worksheet, with new modules (yes, a LOT of copy/pasting), that can clean it up. It didn't.
I also read of an issue where dimensioning arrays had problems when the dimensions were input variables to the function. So I initialized the variables used to set the array dimensions, even though they were input variables to the function. Maybe that's a problem?
The code is really long, so I've only included the call to the second function, the declarations of variables in the second function, and a few other things. Maybe I screwed up some syntax when I passed variables?
The first function:
Option Explicit 'forces all variables to be explicitly declared
Function InputOutputDVL(InData As Variant)
'---------------------------------------------------------------------------------------------
Dim g, p, ng, np, ns, ID, count As Integer
Dim ngmax, npmax, nsmax, namax, nxmax, nymax As Integer
Dim row As Integer
Dim panelmax As Integer
Dim TLstyle As Integer
Dim Group(), Part(), Section(), Airfoil() As Variant
Dim ABP(), TV() As Double
ngmax = 20
npmax = 100
nsmax = 1000
namax = 10
ReDim Group(1 To ngmax, 1 To 4)
ReDim Part(1 To npmax, 1 To 6)
ReDim Section(1 To nsmax, 1 To 17)
ReDim Airfoil(0 To 100, 0 To 2 * namax + 1)
'missing code here
MsgBox ng & " " & np 'This msgbox works correctly and give the right value for np
ABP = Section2VL(nxmax, nymax, ns, panelmax, Group, Part, Section, Airfoil)
MsgBox "Completed Section2VL" 'The code never gets to this msgbox
InputOutputDVL = ABP 'I've tried setting this to = 1234 assuming there was a problem with
'ABP, but the cells on the spreadsheet are still #Value
End Function
The second function:
Option Explicit 'forces all variables to be explicitly declared
Function Section2VL(nxmax, nymax, ns, panelmax, Group, Part, Section, Airfoil)
Dim i, j, k, l, c1, c2 As Integer
Dim g, p, s, ng, np, chord, span, panel, ID, count As Integer
Dim NX, NY As Integer
Dim station, panelID, panelIDref As Integer
Dim pi, Xstyle, Ystyle As Double
Dim angle, dist As Double
Dim sx(), sy() As Double
Dim Scoord(), ABP() As Double
ns = 6
nxmax = 12
nymax = 12
panelmax = 300
ReDim sx(0 To nxmax), sy(0 To nymax)
ReDim Scoord(1 To ns, 0 To nxmax, 1 To 3), ABP(1 To panelmax, 1 To 32)
MsgBox ABP(panelmax, 5) 'This call works, and provides the proper value in the msgbox
'return ABP vector
Section2VL = ABP
'I've also tried just returning an integer thinking there was something wrong with the
'ABP array, like 987, but that doesn't work either
End Function 'This is where it stops when debugging. Doesn't return to first function
Thanks in advance. I've blown two long evenings and can't figure it out.
You issue is incompatable types between InputOutputDVL.ABP, Section2VL.ABP and Section2VL itself
Try declaring all thses () As Double
ie.
In InputOutputDVL
Dim ABP() As Double
In Section2VL
Dim ABP() As Double
And
Function Section2VL(...) As Double()
Note on Type declarations
When you declare variables like this
Dim i, j, k, l, c1, c2 As Integer
all except the last one are infact declared as Variant's
You need to specify each variables type explicitly.
Also, don't use Integer unless you have a specific reason. Use Long instead.
Thanks Chris! You definitely led me in the right direction. I also used these two websites:
http://www.cpearson.com/excel/passingandreturningarrays.htm
http://www.cpearson.com/excel/vbaarrays.htm
Incomplete/incompatible variable declarations were the source of my problem.
First, I had forgotten that VBA requires "as Integer" or "as Double" after EACH variable, not just at the end of the line. So many of variables were VARIANTS instead of integers, doubles, etc. Thanks again Chris!
Second, and more specific, I only had to make one of Chris' changes noted above, and that was to properly declare ABP() as Double in the first calling function. ABP() was already properly called Double in the second function, and I did NOT declare the Function Section2VL as Double(). With the original code:
Dim ABP(), TV() as Double
This indicated that ABP was a VARIANT ARRAY. The later call to ABP = Section2VL() was then trying to stuff a VARIANT into a VARIANT ARRAY, and THAT was the problem. I'm still disappointed that the compiler didn't somehow say it had incompatible data types or some other error. This also explains where the problem came from. The code was previously working as two functions in two modules. As I was making some other changes, I noticed that I hadn't declared ABP as an Array, so I added the "()". I made other changes, and it stopped working. I focused on the other changes, not the seemingly minor "correction" of adding the () marks. What was really happening was that in the original code a combination of mistakes had resulted in a correctly working code! Imagine that!
So the code worked with ABP as a variant, or ABP() as a double array, but NOT with ABP() as a variant array.
Of course, the correct answer here is what Chris suggested, and that is correctly and explicitly declaring all of the variables. An interesting note is that a VARIANT (not VARIANT ARRAY) can essentially "accept" being assigned any incoming value, including a DOUBLE ARRAY.
So in my final code, I've left both Functions as VARIANTs, but I've now specifically declared them as VARIANT to make it clear. In the second function, ABP is properly declared as a dynamic DOUBLE ARRAY, and later given the proper dimensions to allocate memory space. In the first function, ABP could either be a VARIANT or a DOUBLE ARRAY, and either would work, but since I always want it to be a DOUBLE ARRAY, I've specified it as such.
Option Explicit 'forces all variables to be explicitly declared
Function InputOutputDVL(InData As Variant) as Variant
'---------------------------------------------------------------------------------------------
Dim ABP() as Double, TV() As Double 'This ABP was a variant array - which was incompatible
'code removed here
ABP = Section2VL(nxmax, nymax, ns, panelmax, Group, Part, Section, Airfoil)
InputOutputDVL = ABP
End Function
And the second, called function:
Option Explicit 'forces all variables to be explicitly declared
Function Section2VL(nxmax as integer, nymax as integer... ) as Variant
Dim Scoord(), ABP() As Double 'This was already correctly declaring ABP as Double Array, but
'now I realize Scoord was incorrectly a Variant Array, but it
'wasn't actually causing a problem with my code.
'I fixed this in my own code, but left here as example of what
'not to do!
ReDim ABP(1 To panelmax, 1 To 32)
'Code removed here
'return ABP vector
Section2VL = ABP
End Function

VB.Net: Initialising variables with the New constructor

When initialising a list or a queue or a stack or anything similar, which method is preferred?
Dim q as Queue(Of Integer) = New Queue(Of Integer)
or
Dim q as New Queue(Of Integer)
Also, I've started to use New for string and integer variables - is this stupid? Is there any disadvantage to using New rather than just setting the variable to the default setting? E.g.
Dim Num1 As New Integer
Dim Str1 As New String("")
Dim Bool1 As New Boolean
Thank you!
If you'd ask programmers whether they like typing more or less when writing a program then the usual answer is "less!". If you ask them whether they like more or less bugs, the usual answer is "less!" Those are conflicting goals.
The As New syntax has been part of VB.NET for a very long time. It however does come with strings attached, you leave it entirely up to the runtime to figure out whether or not a new object should be created. That does tend to be a bug generator. Sometimes you really do want to create a new object, even though the variable is already assigned. It is also rather ambiguous, in this snippet for example:
For ix As Integer = 0 To 42
Dim q As New Queue(Of Integer)
'' etc...
Next
Question is: do you get one instance of the queue, created in the first pass of the loop or do you get 43 of them? What was actually intended by the programmer? It isn't very clear from the syntax.
There is a 3rd alternative that you overlooked and the one I prefer. Available since VB9 (aka VS2008) called "type inference". Where you don't specify the type of the variable but leave it up to the compiler to figure it out. This option needs to be turned on with Option Infer On, it is turned on by default. It combines the advantages of the abbreviated syntax that As New has and still lets you create the object explicitly in your code with the New statement:
Option Infer On
...
For ix As Integer = 0 To 42
Dim q = New Queue(Of Integer)
'' etc...
Next
Where q is inferred to be of type Queue by the compiler and it is crystal clear that the code generates 43 instances of the queue. The exact equivalent in the C# language is the var keyword.

Weird problem with List(Of T) declaration and .add

I have this really weird problem in my code (.Net Framework 3.5). So, in a normal VB.NET program, I would do something like this :
Dim MyList As New List(Of Integer)
MyList.Add(3)
MyList.Add(5)
MyList.Add(9)
MyList.Add(17)
And if I put a "break" rigth after
Dim MyList As New List(Of Integer)
and put the mouse over "MyList" while code is paused, I should get : Count=0. However, I have a sub where I create a list just that way and get (when I break after it) : "Count = Property evaluation failed".
It seems like if my list has not enough space to hold the data... Here's the complete code I use to try out everything (by the way, the code itself doesn't really make sense since it's a part of my project). With it I can create the "weird" behavior and anyone can test this. Here's the code (sorry the Code Block button isn't working) :
Structure lPossibilitiesOutputStruct
Dim Pinion As GearOutputStruct
Dim Gear As GearOutputStruct
Dim Forces As ForcesStruct
Dim CenterDistance As Double
Dim Pitch As Double
Dim lStagePossibilities As List(Of lPossibilitiesOutputStruct)
End Structure
Structure GearOutputStruct
Dim TeethNbr As Integer
Dim RPM As Double
Dim FaceWidth As Double
Dim OutsideDiameter As Double
Dim Addendum As Double
Dim WholeDepth As Double
Dim OperatingPitchDiameter As Double
Dim OverPinData As OverPinOutputStruct
Dim SpanData As SpanOutputStruct
Dim AllowableBendingPower As Double
Dim AllowablePittingPower As Double
End Structure
Structure OverPinOutputStruct
Dim PinDiameter As Double
Dim OverpinMeasurement As Double
End Structure
Structure SpanOutputStruct
Dim TeethNbr As Integer
Dim SpanMeasurement As Double
End Structure
Structure ForcesStruct
Dim GearSetAxialForce As Double
Dim GearSetRadialForce As Double
Dim GearSetTangentialForce As Double
End Structure
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim lStagePossibilities As New List(Of lPossibilitiesOutputStruct)
Dim MyList As New List(Of Integer)
MyList.Add(3)
MyList.Add(5)
MyList.Add(9)
MyList.Add(17)
lStagePossibilities = lGeneratePossibilities(Nothing, Nothing, 3, 1)
End Sub
Private Function lGeneratePossibilities(ByVal ActualStage As lPossibilitiesOutputStruct, ByVal CSL As List(Of Integer), ByVal MaxStage As Integer, ByVal CumulatedRatio As Double) As List(Of lPossibilitiesOutputStruct)
Return Nothing
End Function
All I did is create a new project with a form and created that code. There's nothing else in the project. I played with the "targeted CPU" and that's all...
If I run that program with compile option to use x86 CPU, and put a break on the "lStagePossibilities = ..." I get the "Count = Property evaluation failed" when i put my mouse over MyList. If I run it with the x64 CPU, then everything is working fine... ("Count = 4"). If I go back with the x86 CPU, I get back the error. Even worst, if I comment the line "Dim Pinion as GearOutputStruct" or the line "Dim Gear as GearOutputStruct" or the line "Dim Forces as ForcesStruct" in the lPossibilitiesOutPutStruct structure declaration, then everything works fine with x86 CPU...
Could it be related to a kind of maximum size of a list ???
Thank you !
Jean
I bet if you changed some of the Strucures into Classes the problem will go away. It is a well known bug in Visual Studio that when Structures get large and try to allocate a lot of local memory things go bad in the debugger. Some of the structures contain other structures and the memory adds up. I bet in x64 the size of the structs gets bigger, due to packing.
To quote some MSDN blogs
Just so others know too. This issue was because of a large struct in my code as well. I converted it to a class and I could read the values of variables hovering over the objects. It worked in some functions and classes, but the error message where the large struct was contained had the error "Cannot evaluate expression because a thread is stopped at a point where garbage collection is impossible, possibly because the code is optimized." and it went away upon making the change. Hope it helps and confirms the solution.
To clarify, what is too big. My Struct has (rather had) ...
55 public variables
A constructor with 6 parameters
Two public functions (one of those was small and one was large, about 200 lines of code)
I believe structs are intended to be used as small containers of straight forward, simple, cohesive data and functionality, and I would agree that this one got too far out of hand. It started out small, of course, and the needs ballooned. Now that it's in a class where the cohesive data and functionality is now contained there, I can sleep at night. =)
It took me all of 5 minutes to convert it to a class. Easy enough.