Metamorphing Machine I rather be this walking metamorphosis
than having that old formed opinion about everything!

Why we can't (fully) simulate inheritance in VB6

The other day I had an epiphany: I could try to fake inheritance in VB6.
Suppose I want to have a BleepingCheckBox. Every time it is clicked, it beeps.
We could start like this:

Class BleepingCheckBox
Implements CheckBox
End Class

We would have to implement twice every method - once to fulfill the Implements part and twice to expose it publicly in our class:

Class BleepingCheckBox
Implements CheckBox

Public Property Get Value As Integer()
Rem Do its thing
End Property

Public Property Let Value(ByVal RHS As Integer)
Rem Do its thing
End Property

Private Property Get CheckBox_Value As Integer()
CheckBox_Value = Value 'Delegate it to the public property
End Property

Private Property Let CheckBox_Value(ByVal RHS As Integer)
Value = RHS 'Delegate it to the public property
End Property

(...)
End Class

Then, we could have a MyBase of sorts:

Class BleepingCheckBox
Implements CheckBox

Private MyBase As CheckBox

Private Sub Class_Initialize()
Set MyBase = New CheckBox
End Sub

Public Property Get Value As Integer()
Value = MyBase.Value
End Property

Public Property Let Value(ByVal RHS As Integer)
MyBase.Value = RHS
End Property

Private Property Get CheckBox_Value As Integer()
CheckBox_Value = Value 'Delegate it to the public property
End Property

Private Property Let CheckBox_Value(ByVal RHS As Integer)
Value = RHS 'Delegate it to the public property
End Property

(...)
End Class

CheckBox has a Click event among others. Let's do it:

Class BleepingCheckBox
Implements CheckBox

Private WithEvents MyBase As CheckBox
Public Event Click()

Private Sub Class_Initialize()
Set MyBase = New CheckBox
End Sub

Public Property Get Value As Integer()
Value = MyBase.Value
End Property

Public Property Let Value(ByVal RHS As Integer)
MyBase.Value = RHS
End Property

Private Property Get CheckBox_Value As Integer()
CheckBox_Value = Value 'Delegate it to the public property
End Property

Private Property Let CheckBox_Value(ByVal RHS As Integer)
Value = RHS 'Delegate it to the public property
End Property

Private Sub MyBase_Click()
Beep
RaiseEvent Click
End Sub

(...)
End Class

It seems to be only a matter of replicating CheckBox's methods and events.
Alas, that's not the case.
As soon as you try to compile it, VB6 gives an error, "Compile error: Bad interface for implements: Method has underscore in name."
It happens that CheckBox has a hidden property, _Default. That underscore is the one that spoils everything.
Every VB6 component has it.

Another issue would be ParamArray. If the class has any method with a ParamArray, then there's no way we can implement it reliably.
The best we can do is:

Public Sub SomeMethod(ParamArray Args())
HackMethod CVar(Args)
End Sub

Private Sub MyBase_SomeMethod(ParamArray Args())
HackMethod CVar(Args)
End Sub

Private Sub HackMethod(Args As Variant)
Select Case UBound(Args)
Case 0
MyBase.SomeMethod Args(0)

Case 1
MyBase.SomeMethod Args(0), Args(1)

Case 2
MyBase.SomeMethod Args(0), Args(1), Args(2)

Case 3
MyBase.SomeMethod Args(0), Args(1), Args(2), Args(3)

Rem How much is enough?
End Select
End Sub

If the class relies on the arguments in ParamArray being passed ByRef, that is lost when we did CVar(Args).
But we needed to do that, as VB6 does not allow us to pass Args as-is; it raises an "Invalid ParamArray use" compiler error when trying to do it.

Not everything is lost, though.
You can follow this path if the class does not have a "bad" method name (meaning underscores), nor any method having ParamArray.
I suppose I should mention that it must not use any unsupported types, like ULong or Any, too.

Next week I'll let you know eighteen things I wished someone had told me before I went into programming.

Andrej Biasic
2021-10-20