Passing a collection byval - vb6

I am looking for since few hours on the Internet but I don't find if it is possible to pass to a function a collection byval ?
I put an example below and as you can see, the function ObjPtr return the same long pointer thus collTemp and myCollection are the same despite ByVal argument.
Is anyone have an idea ?
My code :
private sub function1
dim myCollection as new collection
call myFunction(mycollection)
'For example, ObjPtr(myCollection) = 123456
end sub
'in another class
private sub myFunction(ByVal collTemp as Collection)
'some code
'ObjPtr(collTemp) = 123456
end sub

Objects are not passed "by reference" or even passed at all. What you are passing are object references and these can be passed ByVal or ByRef.
If you pass one ByRef the procedure can modify what object the caller's reference actually references.
If you wish to return a new Collection instance then create one and return a reference to it.

You're passing the collection ByVal, not passing ByVal the value stored inside the collection.
Why not pass by reference, clone the values inside your method, and use the cloned values?
check here
When looking at the Collection as an object passed ByVal, the only thing actually being cloned is the reference to the collection...

I believe ByVal and ByRef don't work with Call inside the same component. Try the same but omit the Call

Related

Attempting to learn polymorphism, etc. in VB6, but my code doesn't do what I want it to

Here's what I've got on a command button; it's just creating variables and attempting to output their ID (which should be an instance variable inherited from the base class.)
Private Sub Command1_Click()
Dim ball1 As Ball, ball2 As Ball
Dim cube1 As Cube, cube2 As Cube
Set ball1 = New Ball
Set cube1 = New Cube
Set cube2 = New Cube
Set ball2 = New Ball
MsgBoxTheID (ball1) 'errors; should be 0
MsgBoxTheID (ball2) 'errors; should be 3
MsgBoxTheID (cube1) 'errors; should be 1
MsgBoxTheID (cube2) 'errors; should be 2
Call ball1.MsgBoxID ' works; displays 0
Call ball2.MsgBoxID ' works; displays 3
Call cube1.MsgBoxID ' works; displays 1
Call cube2.MsgBoxID ' works; displays 2
End Sub
Modeul1.bas:
Global globalID As Integer
Public Sub MsgBoxTheID(theObj As BaseObj)
' this function is meant to accept objects of type Ball, Cube, and BaseObj
MsgBox theObj.ID
End Sub
BaseObj Class Module:
Public ID As Integer
Public isVisible As Boolean
Public Sub setVisiblity(newVis As Boolean)
isVisible = newVis
End Sub
Public Sub MsgBoxID()
MsgBox ID
End Sub
Private Sub Class_Initialize()
ID = globalID
globalID = globalID + 1
End Sub
Ball Class Module:
Implements BaseObj
Private theObj As BaseObj
Public radius As Double
Private Property Let BaseObj_ID(ByVal RHS As Integer)
End Property
Private Property Get BaseObj_ID() As Integer
End Property
Private Property Let BaseObj_isVisible(ByVal RHS As Boolean)
End Property
Private Property Get BaseObj_isVisible() As Boolean
End Property
Public Sub MsgBoxID()
Call theObj.MsgBoxID
End Sub
Private Sub BaseObj_MsgBoxID()
Call theObj.MsgBoxID
End Sub
Public Sub BaseObj_setVisiblity(newVis As Boolean)
End Sub
Private Sub Class_Initialize()
Set theObj = New BaseObj
End Sub
Cube Class Module:
Implements BaseObj
Private theObj As BaseObj
Public sideLength As Double
Private Property Let BaseObj_ID(ByVal RHS As Integer)
End Property
Private Property Get BaseObj_ID() As Integer
End Property
Private Property Let BaseObj_isVisible(ByVal RHS As Boolean)
End Property
Private Property Get BaseObj_isVisible() As Boolean
End Property
Public Sub MsgBoxID()
Call theObj.MsgBoxID
End Sub
Private Sub BaseObj_MsgBoxID()
Call theObj.MsgBoxID
End Sub
Public Sub BaseObj_setVisiblity(newVis As Boolean)
End Sub
Private Sub Class_Initialize()
Set theObj = New BaseObj
End Sub
There are several things I don't like about this, two of which I am of the impression are unavoidable: (1) the fact that it's a mess compared to C++, and (2) the fact that the Ball and Cube classes merely contain an object of BaseObj type. They are not actually inheriting anything from BaseObj; they are only being forced to implement the same interface (whoopty doo.)
To make matters worse, and this is the one that I am truly hoping is rectifiable, they do not seem to be able to fill in for an object of the base class when it comes to parameter passing.
Am I doing something wrong?
Visual Basic 6 is not the ideal language with which to learn the "purer" form of OOP. VB6 was designed to implement a very much hybridized version of object-based programming that orbited the Microsoft Component Object Model (COM) world, with its interface inheritance orientation. VB6 does not support implementation inheritance, which tends to make the kind of polymorphism you're looking for hard to do.
There are a few tricks I recall from the VB6 era to "get around" (sort of) the implementation inheritance problem, particularly when it comes to substituting an object of a base class for a subclass. One trick I remember is to declare a property procedure of the type of the base interface that returns a reference to "Me" as the return type. That "tricks" the runtime into providing the conversion into the desired interface. There's another magic trick to make a property the "defaut" property by setting its "procedure number" to -4 in one of VB6's design dialogs.
The point? If you're really wanting to get into conventional OO programming, don't try to learn it with VB6 if you don't have to. Move up to (at least) VB.NET, C#, or Java. I don't say that as a VB6 hater - heck, knowing these stupid details paid the bills for a long time - but its a tough nut to crack to translate its own little idiosyncrasies into a good, fundamental understanding of OOP.
Good luck!
You've figured out how to fix the error, but I'll contribute the "why".
In VB6 (and VB5, etc), there are two syntaxes for invoking a method/function/subroutine/statement. The first is this:
MySubName arg1, arg2, arg3, arg4
Blech, I know it is my bias from C and Java, but I like to see parenthesis around my argument list. That sytax is this:
Call MySubName(arg1, arg2, arg3, arg4)
So those two are equivilent.
What you ran into is not the effect of the Call. What you ran into is the effect of the unneeded parenthesis in the non-Call version. The parenthesis force the statement/arg inside them to be evaluated before the rest of the statement (think math order of operation).
So this:
SomeSub (arg1)
Is like this:
temp = (arg1)
SomeSub temp
Further, objects in VB6 can have a "default property". This lets you write code like this:
Dim name as String
name = txtName
Instead of assigne the textbox object reference to name, the default property of .Text is used, and the result is like this:
Dim name as String
name = txtName.Text
So when you tried to evaluate SomeSub (arg1) I think it would attempt to locate and execute the default property of your object and pass that value to SomeSub.
Well, I figured it out, sort of.
MsgBoxTheID (ball1) 'errors; should be 0
MsgBoxTheID (ball2) 'errors; should be 3
MsgBoxTheID (cube1) 'errors; should be 1
MsgBoxTheID (cube2) 'errors; should be 2
...needs to be changed to...
Call MsgBoxTheID (ball1) 'errors; should be 0
Call MsgBoxTheID (ball2) 'errors; should be 3
Call MsgBoxTheID (cube1) 'errors; should be 1
Call MsgBoxTheID (cube2) 'errors; should be 2
... even though MsgBoxTheID has no return type, which is weird because I always thought Call was simply something that you could use to discard the return value without having to declare a variable, like so:
dim unneededVar as Integer
unneededVar = FunctionNameThatReturnsAnInteger()
But I guess not. So... I'll have to go read up on exactly what the Call statement is doing to make this example program work, but it's definitely working now. (I also had to add BaseObj_ID = theObj.ID to the Property Get BaseObj_ID() As Integer methods in the classes that were implementing BaseObj.)

What is the difference between Sub and Function in VB6?

I am going through some old VB code and I run into function definitions like these -
Private Function ExistingCustomer(Index As Integer, Customer As String) As Integer
Private Sub cmdCustomerList_Click()
What's the difference?
Function returns value, Sub doesn't. It's that simple.
A function can also be used in an expression. A Subroutine cannot.
Functions can lend to the readability of your code better than a subroutine.
Here's an example of how a function can increase readability:
If AccountIsLocked("JJones") then Msgbox("This account is locked")
this function would be defined somewhere
public function AccountIsLocked(UserId as string) as boolean
dim usr = uow.AccountRepository.UserInfo(UserId)
return usr.locked
end function
Here's the same example but coded with a subroutine:
CheckIfAccountLocked("JJones")
and elsewhere this sub is defined:
public sub CheckIfAccountLocked(UserId)
if uow.AccountRepository.UserInfo(UserId).locked then
msgbox("Account is locked")
end if
end sub
Also note that checking the value is separated from the action -- this contributes to separation of duties. The function would lend toward re-usability.
With VB6 there are some odd rules governing parenthesis. If there are no parameters to a sub then the parenthesis are not needed (I think Visual Studio might remove the parenthesis). One way around this is to add the keyword "Call" before your sub.
Call CheckIfAccountLocked()
vs
CheckIfAccountLocked
In function we can return values as boolean, string and other data data types.
but sub does not return any thing.
it just executes code and instruction that we give. These are also regarded as methods
Sub is also used in control's events and these events also not return any value.
for example the click event of a command button:
Private sub cmd_click()
end sub
They are both sections to write code however a function must return a value. For example if you had a program in which a complicated mathematical procedure needs to be executed a number of times you would simply make a function and have the complicated maths code in there and any time you need to do the calculation you can just call the function. Hope this helped not sure if I explained it well.
What is the difference between Sub and Function in VB6?
"sub" can perform some action.
"sub" returns no value.
Example:
Form_Load()
"function" can also perform some action but it also returns some value to point from which it was called.
that is, "Functions return a value, often based on a variable"
Example:
Val(), FormatPercentage().
function in vb
a function must return some value/s
Syntax :
private function fun_name(argument/s(optional)) as return_type(integer,string..)
return value
end function
fun_name(arguments(optional) ) is enough for function call
sub in vb
a sub need not to be return any value/s
Syntax :
private sub sub_name(argument/s(optional))
end sub
sub_name(arguments(optional) ) is enough for function call
A function holds data and code. But a subroutine contains only code, but not data.
Syntax of functions will be Function...End function and for Sub will be Sub...End Sub.
Functions may or may not have objects but sub doesn't have objects
Functions are re-usable where Sub doesn't
Functions can return values but sub doesn't
Functions may have object repository but sub doesn't
Extension of functions is .qfl where for sub it's .vba

VB6 and .NET - array differences

Please have a look at the following code, which I have run in VB6 and .NET:
Private Sub Form_Load()
Dim TestArray3() As String
TestArray3 = TestArrayFunction
End Sub
Private Function TestArrayFunction() As String()
Dim TestArray1(0 To 1) As String
Dim TestArray2() As String
TestArray1(0) = "Monday"
TestArray1(1) = "Tuesday"
TestArray2 = TestArray1
TestArray1(0) = "Wednesday"
End Function
When the program gets to the end of TestArrayFunction in VB6, the value of TestArray2(0) is "Monday", however when run in .NET, it is "Wednesday". I understand in .NET that an Array is an object and has two references pointing to it in TestArrayFunction. Why is this not the case in VB6?
This is to add to Dabblernl's response.
Long story made short, VB6 never really had a general reference type. Being built on COM, the only reference type it has (Dim ... As Object) is one for COM-based (or COM styled) classes. The Variant type was only good at boxing other types.
As to why ByRef works with arrays ...
The evolution of many dialects of BASIC, including VB6, adopted/adapted the paradigm of FORTRAN when it came to subroutines and functions; namely, parameters are passed by address (hence, ByRef is the default), and functions return their results by value. Add to this that BASIC never really had the concept of an address or a reference: VB6 (and earlier versions) would simulate addresses with 32-bit (signed) integers and otherwise would cope (in strange ways) via peculiar rules of the DECLARE SUB/FUNCTION statement and the (shoe-horned) AddressOf operator and VarPtr/StrPtr functions.
Final note: Since VB6 classes are COM-style, VB6 has the SET statement. The reason for this is that without the SET, an l-value = r-value situation is an implied LET statement. COM supports the notion of a default property (with and without parameters); LET objA = objB is interpreted as LET objA.DefaultPropOfA = objB.DefaultPropOfB. SET, on the other hand, makes objA take on the reference to the same object that objB references.
The Visual Basic of .NET is a nice and powerful language, with far fewer shortcomings and warts than VB6. However, it does have big differences from its legacy cousin. These differences have made long time VB6 users grouchy, and I suspect it has made many VB.NET users confused when they're asked to deal with VB6 code.
BTW, a VB6 coder would deal with the array within a class issue in this way:
Public Property Get DaysOfWeek(ByVal index As Integer) As String
DaysOfWeek = m_strDaysOfWeek(index)
End Property
Public Property Let DaysOfWeek(ByVal index As Integer, ByRef value As String)
m_strDaysOfWeek(index) = value
End Property
That would allow syntax of strDay = clsDateTime.DaysOfWeek(1) and clsDateTime.DaysOfWeek(2)="Tuesday" to work as desired.
I am struggling with this nearly daily. While it is perfectly possible to pass an array ByRef to a Function call, the '=' sign will make a shallow copy.
But there is more strange behaviour of arrays in VB6, Suppose you have the following DateTimeClass classmodule in VB6:
Option Explicit
Private m_strDaysOfWeek() As String
Public Property Get DaysOfWeek() As String()
DaysOfWeek = m_strDaysOfWeek()
End Property
Public Property Let DaysOfWeek(strDaysOfWeek() As String)
m_strDaysOfWeek() = strDaysOfWeek
End Property
Private Sub Class_Initialize()
ReDim m_strDaysOfWeek(7)
m_strDaysOfWeek(1) = "Monday"
End Sub
You would expect to be able to write code like:
Dim clsDateTime As New DateTimeClass
Dim strDay As String
strDay = clsDateTime.DaysOfWeek(1)
Or:
clsDateTime.DaysOfWeek(2)="Tuesday"
But you can't. You have to do it like this:
Dim clsDateTime As New DateTimeClass
Dim strDay As String
Dim strDays() As String
strDays = clsDateTime.DaysOfWeek
strDay = strDays(1)
strDays(2) = "Tuesday"
clsDateTime.DaysOfWeek = strDays
What the reasons of the VB6 team were at the time to implement it thus? I don't know...
I have abandoned the VB6 arrays and use Collections and Dictionaries nearly exclusively.
VB6 copies the array, and the last statement of your Function only changes the original copy.
Since you know .Net broke compatibility with VB I'm not sure where the confusion comes from.

Validate a XDocument against schema without the ValidationEventHandler (for use in a HTTP handler)

(I am new to Schema validation)
Regarding the following method,
System.Xml.Schema.Extensions.Validate(
ByVal source As System.Xml.Linq.XDocument,
ByVal schemas As System.Xml.Schema.XmlSchemaSet,
ByVal validationEventHandler As System.Xml.Schema.ValidationEventHandler,
ByVal addSchemaInfo As Boolean)
I am using it as follows inside a IHttpHandler -
Try
Dim xsd As XmlReader = XmlReader.Create(context.Server.MapPath("~/App_Data/MySchema.xsd"))
Dim schemas As New XmlSchemaSet() : schemas.Add("myNameSpace", xsd) : xsd.Close()
myXDoxumentOdj.Validate(schemas, Function(s As Object, e As ValidationEventArgs) SchemaError(s, e, context), True)
Catch ex1 As Threading.ThreadAbortException
'manage schema error'
Return
Catch ex As Exception
'manage other errors'
End Try
The handler-
Function SchemaError(ByVal s As Object, ByVal e As ValidationEventArgs, ByVal c As HttpContext) As Object
If c Is Nothing Then c = HttpContext.Current
If c IsNot Nothing Then
HttpContext.Current.Response.Write(e.Message)
HttpContext.Current.Response.End()
End If
Return New Object()
End Function
This is working fine for me at present but looks very weak. I do get errors when I feed it bad XML. But i want to implement it in a more elegant way. This looks like it would break for large XML etc.
Is there some way to validate without the handler so that I get the document validated in one go and then deal with errors?
To me it looks Async such that the call to Validate() would pass and some non deterministic time later the handler would get called with the result/errors. Is that right?
Thanks and sorry for any goofy mistakes :).
I have been working with the above code for some time now and I was incorrect about it being Async. It does not move to the next statement before the whole document is validated.

How to use the System.LINQ.Dynamic assembly w/ IEnumerable(Of T)

I'm trying to take the final solution from Phil Haack here and sort using his killer LINQ query but instead of using data context like he is, I want to query against IEnumerable(Of T) ... not having any luck.
Public Function DynamicGridData(ByVal sidx As String, ByVal sord As String, ByVal page As Integer, ByVal rows As Integer) As ActionResult
Dim list As List(Of User) = UserService.GetUserCollection()
Dim FilteredAndSortedList = list.OrderBy(sidx + " " + sord).Skip(pageIndex * pageSize).Take(pageSize)
Return JSON(jsonData)
End Function
Currently I get the error below my .OrderBy(sidx + " " + sord) line
"Data type(s) of teh type parameter(s) in extension method 'Public Function OrderBy(Of TKey)(keySelector As System.Func(Of User, TKey)) As System.Linq.IOrderedEnumerable(Of User)' defined in 'System.Linq.Enumerable' cannot be inferred from these arguments. Specifying the data type(s) explicitly might correct this error."
EDIT:
I found the issue to be that my "list" in the example above was not of type IQueryable. Simply adding .AsQueryable() did the trick!
Public Function DynamicGridData(ByVal sidx As String, ByVal sord As String, ByVal page As Integer, ByVal rows As Integer) As ActionResult
Dim list As List(Of User) = UserService.GetUserCollection()
Dim FilteredAndSortedList = list.AsQueryable().OrderBy(sidx + " " + sord).Skip(pageIndex * pageSize).Take(pageSize)
Return JSON(jsonData)
End Function
I'm not positive but I think it has to do with the fact that your passing into OrderBy a string as opposed to an Expression(TDelegate) which is what it expects.
Take a look at this article and see if it helps.
http://msdn.microsoft.com/en-us/library/bb549264.aspx
Edit:
I've taken a look at Phil's Code and at some further documentation provided by Scott Gu. You should be able to pass in a string in which to order by. That being the case my next question would be if you take out the order by does your app work? The reason for this is I'm wondering if we have another underlying error that we are missing. Like for instance you are returning JSON(jsondata) but I dont see where you are initializing jsondata. Now I dont think that is your error cause you state you are getting it on the orderby statement. However if you are actually getting the error because the object you are trying to order is empty then you have another underlying issue.
this worked for me
http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx

Resources