vb.net what is %Delay% and others - vb.net

So I was looking at some code from github and I came across many instances of code like this :
Dim num As Integer = C_Settings.Delay
Do Until num = 0
Threading.Thread.Sleep(1000)
num -= 1
Loop
What does this loop do? Is it something similar to just a normal sleep function or does this serve some other purpose? (keep in mind this is the first thing that runs when the program is started)
The C_Settings has this inside :
Public Shared Delay As Integer = "%Delay%"
Another example :
Public Shared SPL = C_Settings.SPL
C_Settings :
Public Shared SPL As String = "%SPL%"
I'm pretty new to coding in VB and I have no idea what this does or what this even is. Main question would be : why is it in "%%" and what does it do? I'd be grateful if anyone can explain.

Related

For Each loop encountered 'Collection was modified; enumeration operation may not execute', but I need to change list's size at runtime

I'm attempting to code a Huffman Encoding system in VB, using .NET 6. Here is the main code as it currently stands, using 'ConstructionQueue' as a priority queue to handle the nodes being added to the 'Tree' list. 'Node' is an abstract class from which 'LeafNode' and 'InternalNode' inherit; at the time this class is run, ConstructionQueue is filled with only LeafNode's. My issue is that I need to add InternalNodes to the queue during this initialization process to be handled. Here is the current code:
Public Class HuffmanTree
Private Tree As New List(Of Node)
Public Sub New(ByVal ConstructionQueue As PriorityQueue)
Dim PairMade As Boolean = False
Dim NodesProcessed As Integer = 0
Dim TempWeight As Integer
For Each Node In ConstructionQueue.QueueFrame
If ConstructionQueue.GetFrontPointer <> 0 And ConstructionQueue.GetFrontPointer Mod 2 = 0 Then
PairMade = True
End If
Tree.Insert(NodesProcessed, Node)
If PairMade = True Then
Dim TempNode As New InternalNode(ConstructionQueue.QueueFrame(ConstructionQueue.GetFrontPointer).GetWeight + TempWeight)
TempNode.SetLeftPointer(Tree(NodesProcessed - 1))
TempNode.SetRightPointer(Tree(NodesProcessed))
ConstructionQueue.Enqueue(TempNode)
PairMade = False
Else
TempWeight = Node.GetWeight
End If
NodesProcessed += 1
ConstructionQueue.SetFrontPointer(NodesProcessed)
Next
End Sub
End Class
Collection was modified; enumeration operation may not execute.
is the error I receive on the 'Next' before End Sub. I completely understand why, the issue is that I can't alter the size of the 'QueueFrame' List, but I need a method of doing this that is going to allow me to go through each node in the list whilst being able to change its size, as there will be multiple instances where I need to add to the list as I'm using it.
Does anyone know of any sort of workaround or fix to this? I'd be happy to provide any more information needed.

Class variable as the counter of a For loop in VBA

I have a class module called MyClass, with a public integer in it:
Public i as Integer
When I try to use this variable in a For loop like so:
Dim MyInstance as MyClass: Set MyInstance = New MyClass
For MyInstance.i = 1 To 10
Debug.Print "Hello"
Next
I get the error: Variable required. Can't assign to this expression
I have consulted the help page but cannot see how it applies to my case. The relevant fragment is: "You tried to use a nonvariable as a loop counter in a For...Next construction. Use a variable as the counter." But i is a variable after all, and not a Let Property function or any other expression.
What is wrong with the code?
EDIT: I should point out that the reason I want my iterator to be part of the class is that I have multiple instances of the class, serving different purposes in my project, and there are multiple nested For loops for each instance of the class. Therefore it is worth having the iterators belong to their respective objects, say:
For Client.i = 1 To Client.Count
For Order.i = 1 To Order.Count
For Item.i = 1 To Item.Count
etc.
I have settled for the following workaround but am still not entirely satisfied with it:
For ciii = 1 To Client.Count
Client.i = ciii ' Client.i is later used in the code
For oiii = 1 To Order.Count
Order.i = oiii
For iiii = 1 To Item.Count
Item.i = iiii
You cannot use MyInstance.i as the increment counter but you can use it as the terminator; e.g. For i = 1 To MyInstance.i.
MyClass class
Option Explicit
Public pi As Long
Public Property Get i() As Long
i = pi
End Property
Public Property Let i(Value As Long)
pi = Value
End Property
test sub procedure in Module1
Sub test()
Dim MyInstance As MyClass, i As Long
Set MyInstance = New MyClass
MyInstance.i = 10
For i = 1 To MyInstance.i
Debug.Print "Hello"
Next
End Sub
If you want a publicly accessible loop variable stick it at the top of a standard module i.e. declare the Public i at the top of a standard module.
Note that this would mean you need to re-write your standard module code as, as per point two, you are treating i as if it is a property/method of the class.
So, standard module code would be:
Public i As Long
Sub ........
For i = 1 To 10
Debug.Print "Hello"
Next i
End Sub ......
If you want it to somehow be a property/method then you need to define Getters and Setters (potentially) in the class. And then re-write your module code accordingly. Especially if you are planning on looping using i, you will need an incrementor method in the class.
And yes, I have changed i to Long as there are no advantages, in this case I believe, of having it declared as Integer. A Long is a safer bet for avoiding potential overflow.
If you need a workaround so that you iterate through a property of the instance, you could create a method to increment it, change your loop to a Do While ... Loop and call that method before the loop call.
'Class Module
Option Explicit
Public i As Integer
Public Sub increment_i()
i = i + 1
End Sub
Private Sub Class_Initialize()
i = 0
End Sub
'Module
Sub loop_myclass()
Dim instance As MyClass: Set instance = New MyClass
Do While instance.i <= 10
'Instance property dependent code here
Debug.Print instance.i
instance.increment_i
Loop
End Sub
OK, I found the answer. There is a Microsoft help page on For…Next loop regarding VB, but I think it pertains to VBA as well.
It says:
If the scope of counter isn't local to the procedure, a compile-time
warning occurs.
So there's not much to discuss here, it's just the way MS wants it to be. Though I'd think that if the scope is greater than the procedure it shouldn't cause any problems, but apparently it does.

Can I create variables dynamically based on other variables?

I'm working on a VBA module to process lists of quote items. My current boggle is trying to stack full or partial sets of things from the quote lists, and I'm trying to figure out how to keep track of them.
The item lists do not have a consistent number of items; one might be a single item, another might be a hundred.
The system divides the cargo into four broad types (Pipes, Plates, Beams and Other) for the sake of selecting which calculator logic to use.
Is there any way to create variables on the fly to keep track of individual line items? For instance, deploying a spot of pseudocode:
Public Qty & "_" & Class & "-" & ClassCount As Integer
Is there any way to make something like that work, or is there a better way to do it?
I'm a bit sketchy on classes, and I really should start looking at them more as they're very powerful - this link will give you more info: http://www.cpearson.com/excel/classes.aspx
Expanding on Jasons comments this is one way of building the class, and I'm sure there's a much better way of doing it:
Add a Class Module to your project and name the module cls_Quote.
Add this code to the class module:
Option Explicit
Private sQuote As String
Private lQuantity As Long
Private lAnotherValue As Long
Public Property Let Quote(Value As String)
sQuote = Value
End Property
Public Property Get Quote() As String
Quote = sQuote
End Property
Public Property Let Quantity(Value As Long)
lQuantity = Value
End Property
Public Property Get Quantity() As Long
Quantity = lQuantity
End Property
Public Property Let AnotherValue(Value As Long)
lAnotherValue = Value
End Property
In a normal module add this code:
Option Explicit
Private MyQuotes As Collection
Public Sub Test()
Dim MyNewQuote As cls_Quote
Dim x As Long
Dim vIQuote As Variant
Dim FinalSum As Long
Set MyQuotes = New Collection
For x = 1 To 10
Set MyNewQuote = New cls_Quote
With MyNewQuote
.Quantity = x
.Quote = "Pipes"
.AnotherValue = x * 5
End With
MyQuotes.Add MyNewQuote
Next x
For Each vIQuote In MyQuotes
If vIQuote.Quote = "Pipes" Then
FinalSum = FinalSum + vIQuote.Quantity
End If
Next vIQuote
MsgBox "Total sum of Pipes is: " & FinalSum
End Sub
Note: In the For x loop I'm creating a new instance of the class each time.
Now just waiting for someone with more class programming experience to show a much better way of doing it. :)

Threading Exception: The number of WaitHandles must be less than or equal to 64

The title is to make this easy to find for others having this error. I'm new to Threading, so this is really giving me heck. I'm getting this runtime error that crashed Cassini. This is code that I'm maintaining originally developed as a website project in VS 2003 and converted to a VS 2008 website project.
Important Info:
The number of objects in the manualEvents array is 128 in this case.
products is an array of Strings
Need to support .NET 2.0
For Each product As String In products
If Not product.Trim().ToUpper().EndsWith("OBSOLETE") Then
calls += 1
End If
Next
Dim results(calls - 1) As DownloadResults
'Dim manualEvents(calls - 1) As Threading.ManualResetEvent '128 objects in this case.
Dim manualEvents(0) As Threading.ManualResetEvent
manualEvents(0) = New Threading.ManualResetEvent(False)
'NOTE: I don't think this will work because what is not seen here, is that
' this code is being used to populate and cache a long list of products,
' each with their own category, etc. Am I misunderstanding something?
'initialize results structures
'spawn background workers
calls = 0
For Each product As String In products
If Not product.Trim().ToUpper().EndsWith("OBSOLETE") Then
Dim result As New DownloadResults
'manualEvents(calls) = New Threading.ManualResetEvent(False)
'Moved above For Each after declaration of variable
result.params.product = product
result.params.category = docType
'result.ManualEvent = manualEvents(calls)
result.ManualEvent = manualEvents(0)
result.Context = Me._context
results(calls) = result
Threading.ThreadPool.QueueUserWorkItem(AddressOf ProcessSingleCategoryProduct, results(calls))
Threading.Interlocked.Increment(calls) 'Replaces below incrementation
'calls += 1
End If
Next
Threading.WaitHandle.WaitAll(manualEvents) 'CRASHES HERE
Thread Helper Function (for the sake of completion)
Public Shared Sub ProcessSingleCategoryProduct(ByVal state As Object)
Dim drs As DownloadResults = CType(state, DownloadResults)
Dim adc As New cADCWebService(drs.Context)
drs.docs = adc.DownloadADC(drs.params.category, drs.params.product)
drs.ManualEvent.Set()
End Sub
You don't need an array of 128 manual events to check for completion of all 128 threads.
Create only one manual reset event and a plain integer starting at 128. Decrement that integer using Interlocked.Decrement at the end of ProcessSingleCategoryProduct, and only signal the event when the count reaches zero:
if (Interlocked.Decrement(ByRef myCounter) = 0) myEvent.Set();
Then declare only one Threading.ManualResetEvent as opposed to an array of them, and you can call WaitOne instead of WaitAll on it, and you are done.
See also usr's comment for an easier alternative in case you have .NET 4.

Get the Integer value of an enumeration which is a generic

Here is the basic situation.
Public Class MyEnumClass(of T)
Public MyValue as T
End Class
This is vast oversimplification of the actual class, but basically I know that T is an enumeration (if it is not then there will be many other problems, and is a logical error made by the programmer)
Basically I want to get the underlying integer value of MyValue.
Using Cint or Ctype, does not work.
I was going to use a cool piece of reflection code but just a simple Convert.ToInt32 works great... Forgive my VB I'm a C# guy
Public Function GetEnumInt(Of T)(enumVal As T) As Integer
Return Convert.ToInt32(enumVal)
End Function
I tried this and it worked:
String.Format("{0:d}", MyValue)
I know you can do the following to get all the underlying values (I hope my VB syntax is correct... I've been working in C# mostly of late):
Dim intVal As Integer
For Each intVal In [Enum].GetValues(GetType(T))
//intValue is now the enum integer value
Next
That might at least get you started in the right direction.
This also works :
Fix(enumval)
Another simple way in VB.NET is to add it to 0:
Dim intVal As Integer = 0 + myEnum
So, this should work:
Sub GetEnumInt(of T)(enumVal as T) as Int
return 0 + enumVal
End Sub
Thanks to 'Jon Skeet'. But his code does not work in my Excel-2016. Minwhile the next code works fine:
Public Enum TypOfProtectWs
pws_NotFound = 0
pws_AllowAll = 1
pws_AllowFormat = 2
pws_AllowNone = 3
End Enum
Private Function TypOfProtectWs2I(pws As TypOfProtectWs) As Integer
TypOfProtectWs2I = Format("0", pws)
End Function
Private Sub test_TypOfProtectWs2I()
Debug.Print TypOfProtectWs2I(pws_AllowAll)
End Sub