Error 91 in VBA using classes - class

I'm currently trying to implement a Clark & Wright Savings Heurisitc in VBA, but I'm currently facing some problem. I'm fairly new to VBA, and this error (91) keeps apearing on similar situations, which lead me to believe I'm missing some crucial knowledge. Next I present you the code:
Public Sub CWsavings()
Dim i As Integer
Dim j As Integer
Dim k As Integer
Dim aux As Integer
Dim d As Integer
Dim r As Integer
Dim Cu(200) As customer
Dim De(12) As Depot
For i = 1 To 200
Set Cu(i) = New customer
Cu(i).custID = i
Cu(i).longitude = ThisWorkbook.Sheets("Folha1").Cells(i + 1, 2)
Cu(i).latitude = ThisWorkbook.Sheets("Folha1").Cells(i + 1, 3)
Cu(i).lt = ThisWorkbook.Sheets("Folha1").Cells(i + 1, 4)
Cu(i).et = ThisWorkbook.Sheets("Folha1").Cells(i + 1, 5)
Cu(i).weekdemand = ThisWorkbook.Sheets("Folha1").Cells(i + 1, 6)
Cu(i).peakdemand = ThisWorkbook.Sheets("Folha1").Cells(i + 1, 7)
Cu(i).D1 = ThisWorkbook.Sheets("Folha1").Cells(i + 1, 8)
Cu(i).D2 = ThisWorkbook.Sheets("Folha1").Cells(i + 1, 9)
Next i
For j = 1 To 12
Set De(j) = New Depot
De(j).depotID = j
De(j).Dname = ThisWorkbook.Sheets("Folha1").Cells(i + 1, 13)
De(j).latitude = ThisWorkbook.Sheets("Folha1").Cells(i + 1, 14)
De(j).longitude = ThisWorkbook.Sheets("Folha1").Cells(i + 1, 15)
De(j).ncust = ThisWorkbook.Sheets("Results").Cells(j, 7)
De(j).nroute = 0
For k = 1 To De(j).ncust
aux = ThisWorkbook.Sheets("Results").Cells(j + 1, 10 + k)
Call De(j).SetCustomer(Cu(aux), k)
Next k
Next j
For d = 1 To 12
Dim M(30, 30) As Double
Dim maxsav As Double
Dim maxpos(2) As Integer
Dim connorder(676, 2) 'order of connections for routing
Dim it As Integer
it = 0
For i = 1 To De(d).ncust
For j = 1 To De(d).ncust
M(i, j) = CalcSavings(De(d), De(d).customer(i), De(d).customer(j)) ' error here
Next j
Next i
itbegin:
maxsav = 0
maxpos(1) = 0
maxpos(2) = 0
For i = 1 To De(d).ncust
For j = 1 To De(d).ncust
If i <> j Then
If M(i, j) > maxsav Then
maxsav = M(i, j)
maxpos(1) = i
maxpos(j) = j
End If
End If
Next j
Next i
it = it + 1
connorder(it, 1) = maxpos(1)
connorder(it, 2) = maxpos(2)
If it < De(d).ncust * De(d).ncust - ncust Then
M(maxpos(1), maxpos(2)) = 0
GoTo itbegin
End If
Next d
End Sub
Public Function CalcSavings(d As Depot, C1 As customer, C2 As customer)
Dim id As Double
Dim dj As Double
Dim ij As Double
id = DeptDist(C1, d)
dj = DeptDist(C2, d)
ij = CustDist(C1, C2)
CalcSavings = id + dj - ij
End Function
The class Depot:
Public depotID As Integer
Public Dname As String
Public latitude As Double
Public longitude As Double
Private customers(200) As customer
Public ncust As Integer
Private routes(500) As route
Public nroute As Integer
Public Sub addcust(C As customer)
ncust = ncust + 1
Set customers(ncust) = C
End Sub
Public Sub addroute(R As route)
nroute = Me.nroute + 1
Set routes(Me.nroute) = R
End Sub
Public Property Get customer(i As Integer) As customer
customer = customers(i)
End Property
Public Sub SetCustomer(C As customer, i As Integer)
Set customers(i) = C
End Sub
Public Property Get route(i As Integer) As route
route = routes(i)
End Property
Public Sub SetRoute(R As route, i As Integer)
Set routes(i) = R
End Sub
(Class depot Updated)
And the class Customer:
Public custID As Integer
Public latitude As Double
Public longitude As Double
Public lt As Double
Public et As Double
Public weekdemand As Integer
Public peakdemand As Integer
Public D1 As Integer
Public D2 As Integer
I'm sorry for the long post, any help would be appreciated.

Final answer...
VERY ODDLY, (not that odd, when you really look at it, but) you need to use Set even in your Get properties. I guess the reason behind this is because you're returning an object, and even though that object may already exist, you're not going to use that very object. A copy is used instead and Set becomes vital to initialize that copy.
For example, here's what your "get customer" should look like :
Public Property Get customer(i As Integer) As customer
Set customer = customers(i)
End Property
I guess it all makes sense; your array is private, and therefore you wouldn't want to pass the exact object that is contained inside that array, or it'd be counter-logic.
I think I found it... again...!
Try this :
Public Sub SetCustomer(C As customer, i As Integer)
Set customers(i) = C
End Sub
Notice customer(i) was replaced by customers(i)
EDIT : Deleted previous answer, as I was mostly fishing.

Related

How can I make a code that tells VB6 that tells VB6 that you're inputting something other than Whole numbers?

Introduction: Hi, everyone! I'm new to VB6! How can I make a code that tells VB6 that tells VB6 that you're inputting something other than Whole numbers?
Details: I'm making an arithmetic progression calculator (I think the code is not needed? but I'll just provide just in case.) Here is my code:
Option Explicit
Private Sub btCalc_Click()
Dim A As Long
Dim N As Long
Dim D As Long
Dim R As Long
Dim F As Long
A = Val(txtInitterm.Text)
N = Val(txtTermint.Text)
D = Val(txtFinterm.Text)
R = Val(txtTermint.Text)
F = N / 2 * (2 * A + (N - 1) * D)
lblOutput.Caption = F
End Sub
and I wanna notify or tell VB6 that I'm putting in a fraction, not an integer and uses that fraction to do operations.
NOTE: String Fraction to Value in VBA this doesn't answer my question... :D
Thank you for helping me, everyone! it's much appreciated.
There is no Application.Evaluate(...) in Vb6 like in VBA, so you have to do it like the "question" in "String Fraction to Value in VBA". Extract the logic to a function for re-use, and replace the Val(...) calls with the function for use.
Something like below would likely work, although you may want to provide better error handling in the obvious bad-math cases. I simply return zero and mark them with a comment.
Option Explicit
Private Sub btCalc_Click()
Dim A As Long, N As Long, D As Long, R As Long, F As Long
A = GetFrac(txtInitterm)
N = GetFrac(txtTermint)
D = GetFrac(txtFinterm)
R = GetFrac(txtTermint)
F = N / 2 * (2 * A + (N - 1) * D)
lblOutput.Caption = F
End Sub
Public Function GetFrac(ByVal S As String) As Double
GetFrac = 0 ' default return on error
If InStr(S, "/") = 0 Then GetFrac = Val(S): Exit Function
Dim P() As String, N As Double, D As Double
P = Split(S, "/")
If UBound(P) <> 1 Then Exit Function ' bad input -- multiple /'s
N = Val(P(0))
D = Val(P(1))
If D = 0 Then Exit Function ' div by 0
GetFrac = N / D
End Function

Are unsigned long types available to LibreOffice Basic?

I'm want to write a simple 32-bit FNV hash function for LibreOffice Calc. However, LibreOffice Basic only supports signed long data types, so you will get an "Inadmissible value or data type. Overflow." error on line 7 with the following code:
Function Hash(strText as String) as Long
Dim h As Long
Dim nextChar As String
Dim temp As Long
h = 2166136261
For i = 1 To Len(strText)
nextChar = Mid(strText, i, 1)
temp = Asc(nextChar)
h = h XOR temp
h = h * 16777619
Next
Hash = h
End Function
Because the h variable is assigned 2166136261 in the code above, it is obviously out of bounds. Is it possible to work with unsigned long (0 to 4294967295) data types in LibreOffice Basic? If so, how?
You could do this:
Sub CallHash
oMasterScriptProviderFactory = createUnoService(_
"com.sun.star.script.provider.MasterScriptProviderFactory")
oScriptProvider = oMasterScriptProviderFactory.createScriptProvider("")
oScript = oScriptProvider.getScript(_
"vnd.sun.star.script:foo.py$hash?language=Python&location=user")
hashString = oScript.invoke(Array("bar"), Array(), Array())
MsgBox hashString
End Sub
foo.py:
def hash(strText):
h = 2166136261
for nextChar in strText:
temp = ord(nextChar)
h = h ^ temp
h = h * 16777619
return str(h)
Or drop Basic and use only Python-UNO.
There are unsigned long values in the UNO API. However, I didn't find any API methods to perform calculations on this object.
Dim o As Object
o = CreateUnoValue("unsigned long", 2166136261)

Load event isn't working. Visual Basic

I'm trying to write a lottery application and have written out all the code. Everything seems to be fine except the part where it's supposed to show the winning numbers and the user's numbers. When the ResultsForm pops up, all the labels are blank instead of having the numbers filled in. Any help would be greatly appreciated. I will include the code for the MainForm, the ResultsForm and the Module I used.
Public Class MainForm
Dim rand As New Random
Private Sub MainForm_Load(ByVal sender As System.Object, ByVal e As System.EventArgs)
GenerateNewLotteryNumbers()
End Sub
Private Sub btnCheckNumbers_Click(sender As Object, e As EventArgs) Handles btnCheckNumbers.Click
If ValidateUserPicks() Then
DisplayResults()
End If
End Sub
Private Sub btnPlayAgain_Click(sender As Object, e As EventArgs) Handles btnPlayAgain.Click
GenerateNewLotteryNumbers()
Reset()
End Sub
Private Sub btnExit_Click(sender As Object, e As EventArgs) Handles btnExit.Click
Me.Close()
End Sub
Sub GenerateNewLotteryNumbers()
For intCount = 0 To g_intUPPER_SUB
g_intNumbers(intCount) = rand.Next(10)
Next
End Sub
Sub Reset()
txtDigit1.Clear()
txtDigit2.Clear()
txtDigit3.Clear()
txtDigit4.Clear()
txtDigit5.Clear()
txtDigit1.Focus()
End Sub
Sub DisplayResults()
Dim frmResults As New ResultsForm
frmResults.ShowDialog()
End Sub
Function ValidateUserPicks() As Boolean
Dim blnIsValid As Boolean = False
If Integer.TryParse(txtDigit1.Text, g_intUserPicks(0)) And
g_intUserPicks(0) >= 0 And g_intUserPicks(0) <= 9 Then
If Integer.TryParse(txtDigit2.Text, g_intUserPicks(1)) And
g_intUserPicks(1) >= 0 And g_intUserPicks(1) <= 9 Then
If Integer.TryParse(txtDigit3.Text, g_intUserPicks(2)) And
g_intUserPicks(2) >= 0 And g_intUserPicks(2) <= 9 Then
If Integer.TryParse(txtDigit4.Text, g_intUserPicks(3)) And
g_intUserPicks(3) >= 0 And g_intUserPicks(3) <= 9 Then
If Integer.TryParse(txtDigit5.Text, g_intUserPicks(4)) And
g_intUserPicks(4) >= 0 And g_intUserPicks(4) <= 9 Then
End If
blnIsValid = True
Else
MessageBox.Show("Digit 5: Enter a number between 0 and 9.")
txtDigit5.Focus()
txtDigit5.SelectAll()
End If
Else
MessageBox.Show("Digit 4: Enter a number between 0 and 9.")
txtDigit4.Focus()
txtDigit5.SelectAll()
End If
Else
MessageBox.Show("Digit 3: Enter a number between 0 and 9.")
txtDigit3.Focus()
txtDigit3.SelectAll()
End If
Else
MessageBox.Show("Digit 2: Enter a number between 0 and 9.")
txtDigit2.Focus()
txtDigit2.SelectAll()
End If
Return blnIsValid
End Function
End Class
ResultsForm:
Public Class ResultsForm
Private Sub ResultsForm_Load(ByVal sender As System.Object, ByVal e As System.EventArgs)
DisplayLotteryNumbers()
ShowResults()
End Sub
Private Sub btnOk_Click(sender As Object, e As EventArgs) Handles btnOk.Click
Me.Close()
End Sub
Sub DisplayLotteryNumbers()
lblWinningNumber1.Text = g_intNumbers(0).ToString
lblWinningNumber2.Text = g_intNumbers(1).ToString
lblWinningNumber3.Text = g_intNumbers(2).ToString
lblWinningNumber4.Text = g_intNumbers(3).ToString
lblWinningNumber5.Text = g_intNumbers(4).ToString
lblYourNumber1.Text = g_intNumbers(0).ToString
lblYourNumber2.Text = g_intNumbers(1).ToString
lblYourNumber3.Text = g_intNumbers(2).ToString
lblYourNumber4.Text = g_intNumbers(3).ToString
lblYourNumber5.Text = g_intNumbers(4).ToString
End Sub
Sub DisplayWinnerForm()
Dim frmWinner As New WinnerForm
frmWinner.ShowDialog()
End Sub
Sub ShowResults()
Select Case MatchingDigits()
Case 1
lblResults.Text = "One Matching Digit."
Case 2
lblResults.Text = "Two Matching Digits."
Case 3
lblResults.Text = "Three Matching Digits."
Case 4
lblResults.Text = "Four Matching Digits."
Case 5
lblResults.Text = "All Digits Match!"
DisplayWinnerForm()
Case Else
lblResults.Text = "No Matching Digits."
End Select
End Sub
Function MatchingDigits() As Integer
Dim numMatches As Integer
For intCount = 0 To g_intUPPER_SUB
If g_intNumbers(intCount) = g_intUserPicks(intCount) Then
numMatches += 1
End If
Next
Return numMatches
End Function
End Class
Module:
Module LotteryModule
Public Const g_intUPPER_SUB As Integer = 4
Public g_intNumbers(g_intUPPER_SUB) As Integer
Public g_intUserPicks(g_intUPPER_SUB) As Integer
End Module

Link Variable to Label on another form in vb

I have a small program for some college coursework, i have to enter data of the gender and age of a group of people then work out the male and female percent and the child and adult percent. The bit i am stuck with is i want to display my results on labels on a separate form. Here is my code:
Public Class Form1
Dim TotalGender As Integer
Dim TotalAge As Integer
Dim MaleCount As Integer
Dim FemaleCount As Integer
Dim ChildCount As Integer
Dim AdultCount As Integer
Dim MalePercent As Single
Dim FemalePercent As Single
Dim AdultPercent As Single
Dim ChildPercent As Single
Private Sub btnSub_Click(sender As Object, e As EventArgs) Handles btnSub.Click
If cmbGender.Text = "Male" Then
MaleCount += 1
End If
If cmbGender.Text = "Female" Then
FemaleCount += 1
End If
If cmbAge.Text > 18 Then
ChildCount += 1
End If
If cmbAge.Text <= 18 Then
AdultCount += 1
End If
End Sub
Private Sub btnResults_Click(sender As Object, e As EventArgs) Handles btnResults.Click
Form2.Show()
MalePercent = MaleCount / TotalGender * 100
FemalePercent = FemaleCount / TotalGender * 100
AdultPercent = AdultCount / TotalAge * 100
ChildPercent = ChildCount / TotalAge * 100
End Sub
Private Sub btnReset_Click(sender As Object, e As EventArgs) Handles btnReset.Click
TotalGender = 0
TotalAge = 0
MaleCount = 0
FemaleCount = 0
ChildCount = 0
AdultCount = 0
MalePercent = 0
FemalePercent = 0
AdultPercent = 0
ChildPercent = 0
End Sub
End Class
My second form has all the labels already placed and i know how to display results on a label, i just don't know how to transfer the results across to another form
You can write on form 1 .It shows your results on form2
Form2.Label1.Text = AdultCount
Form2.Show()

How can I convert Date() to dd-monthname-YYYY in ASP Classic?

I searched but couldn't find what I'm looking for.
How do I convert a normal Date() in ASP Classic to a string in the format dd-monthname-YYYY?
Here is an example:
Old date (mm/dd/YYYY) : 5/7/2013
New date (dd-monthname-YYYY) : 7-May-2013
Dim Dt
Dt = CDate("5/7/2013")
Response.Write Day(Dt) & "-" & MonthName(Month(Dt)) & "-" & Year(Dt)
' yields 7-May-2013
' or if you actually want dd-monthname-YYYY instead of d-monthname-YYYY
Function PadLeft(Value, Digits)
PadLeft = CStr(Value)
If Len(PadLeft) < Digits Then
PadLeft = Right(String(Digits, "0") & PadLeft, Digits)
End If
End Function
Response.Write PadLeft(Day(Dt), 2) & "-" & MonthName(Month(Dt)) & "-" & Year(Dt)
'yields 07-May-2013
I wrote an ASP Classic date handling object a while back that might be of use to you. It has a .Format() method that lets you pass in format specifiers just like the Format() function from VB/VBA. If there are any parts missing, I apologize--but this should be a giant leap forward toward natural date formatting.
Private pMillisecondMatch
Function RemoveMillisecondsFromDateString(DateString) ' Handle string dates from SQL Server that have milliseconds attached
If IsEmpty(pMillisecondMatch) Then
Set pMillisecondMatch = New RegExp
pMillisecondMatch.Pattern = "\.\d\d\d$"
pMillisecondMatch.Global = False
End If
RemoveMillisecondsFromDateString = pMillisecondMatch.Replace(DateString, "")
End Function
Function DateConvert(DateValue, ValueIfError)
On Error Resume Next
If IsDate(DateValue) Then
DateConvert = CDate(DateValue)
Exit Function
ElseIf TypeName(DateValue) = "String" Then
DateValue = RemoveMillisecondsFromDateString(DateValue)
If IsDate(DateValue) Then
DateConvert = CDate(DateValue)
Exit Function
End If
End If
DateConvert = ValueIfError
End Function
Class AspDate
Private pValue
Public Default Property Get Value()
Value = pValue
End Property
Public Property Set Value(DateValue)
If TypeName(DateValue) = "AspDate" Then
pValue = DateValue.Value
Else
Err.Raise 60020, "Class AspDate: Invalid object type " & TypeName(DateValue) & " passed to Value property."
End If
End Property
Public Property Let Value(DateValue)
pValue = DateConvert(DateValue, Empty)
End Property
Public Property Get FormattedDate()
FormattedDate = Format("yyyy-mm-dd hh:nn:ss")
End Property
Public Function Format(Specifier)
Dim Char, Code, Pos, MonthFlag
Format = "": Code = ""
If IsEmpty(Value) Then
Format = "(Empty)"
End If
Pos = 0
MonthFlag = False
For Pos = 1 To Len(Specifier) + 1
Char = Mid(Specifier, Pos, 1)
If Char = Left(Code, 1) Or Code = "" Then
Code = Code & Char
Else
Format = Format & Part(Code, MonthFlag)
Code = Char
End If
Next
End Function
Private Function Part(Interval, MonthFlag)
Select Case LCase(Left(Interval, 1))
Case "y"
Select Case Len(Interval)
Case 1, 2
Part = Right(CStr(Year(Value)), 2)
Case 3, 4
Part = Right(CStr(Year(Value)), 4)
Case Else
Part = Right(CStr(Year(Value)), 4)
End Select
Case "m"
If Not MonthFlag Then ' this is a month calculation
MonthFlag = True
Select Case Len(Interval)
Case 1
Part = CStr(Month(Value))
Case 2
Part = Right("0" & CStr(Month(Value)), 2)
Case 3
Part = MonthName(Month(Value), True)
Case 4
Part = MonthName(Month(Value))
Case Else
Part = MonthName(Month(Value))
End Select
Else ' otherwise it's a minute calculation
Part = Right("0" & Minute(Value), 2)
End If
Case "n"
Part = Right("0" & Minute(Value), 2)
Case "d"
Part = CStr(Day(Value))
If Len(Part) < Len(Interval) Then
Part = Right("0" & Part, Len(Interval))
End If
Case "h"
MonthFlag = True
Part = CStr(Hour(Value))
If Len(Part) < Len(Interval) Then
Part = Right("0" & Part, Len(Interval))
End If
Case "s"
Part = Right("0" & Second(Value), 2)
Case Else ' The item is not a recognized date interval, just return the value
Part = Interval
End Select
End Function
End Class
Function NewDate(Value)
Set NewDate = New AspDate
NewDate.Value = Value
End Function
Function NewDateWithDefault(Value, DefaultValue)
Set NewDateWithDefault = New AspDate
If Value = Empty Then
NewDateWithDefault.Value = DefaultValue
Else
NewDateWithDefault.Value = Value
End If
End Function
Here's example code using the above class:
<%=NewDate(Checkin.Parameters.Item("#DOB").Value).Format("mm/dd/yyyy")%>
To get the format you've noted above, you would do:
.Format("d-mmmm-yyyy")