VB6 pass by value and pass by reference

I am struggling to understand pass by value and pass by reference in VB6. I understand these concepts fully in Object Oriented programming languages like .NET and Java (I realise that Java doesn't have pass by reference). Have a look at the code below:

Private Sub Form_Load()

Dim Test As Integer
Test = 1
TestFunction Test 'line 5
MsgBox (Test)

End Sub

Private Sub TestFunction(ByVal i As Integer)
    i = i + 1
End Sub

When I put brackets around Test on line 5, then the message box prints 1 as I would expect. Now have a look at the code below:

Private Sub Form_Load()

Dim Test As Integer
Test = 1
TestFunction Test 'line 5
MsgBox Test

End Sub

Private Sub TestFunction(ByRef i As Integer)
    i = i + 1
End Sub

The message box prints 2 as I would expect. However, if you add brackets to line 5 then the message box prints 1 as I would not expect. It appears that the calling function can pass by value even if the variable defined in the called function is ByRef. It appears not to be the case vice versa, i.e. if the called function has a signature with a variable defined as ByVal then it will always be ByVal (even if there are no brackets around the variable in the calling function). What is the thinking behind this in VB6? I realize that this is a basic question in VB6 but it has confused me. I have read the MSDN documentation and I realize that this is all true, however it does not explain the reasoning behind it.


This is a classic gotcha in VB6. It is explained in the VB6 manual. In this code below, VB6 treats the argument as an expression (Test) rather than a variable reference

TestFunction (Test)

In order to pass a reference to the variable either omit the brackets or use the legacy Call statement (which requires brackets)

TestFunction Test
Call TestFunction(Test)

VB6 allows you to pass expressions to ByRef arguments even if the method changes them. Eg you can write

TestFunction (Test + 2)

The compiler creates a temporary copy and passes that by reference. VB.Net uses brackets in a similar way.

You can also get the compiler to create temporary copies if TestFunction takes two arguments like this:

TestFunction (one), (two)

And you can get temporary copies even with Call if you double your brackets, adding an extra unecessary pair:

Call TestFunction((Test))

Enclosing any expression within parentheses causes that expression to be evaluated first before doing anything else, even when that expression is only a single variable. In your case, the result of that expression is then passed as an argument.

So, you are in fact passing the argument by reference. But the argument you are passing is the result of the expression and not the original variable. This is why the original variable does not get updated.