The CDrives class, version 2
The CDrives class has no special relationship with the CDrive class. It uses CDrive objects as needed, but it knows only the public methods of CDrive—something any other class could know. But the CDrives class does have a close and intimate relationship with the CDriveWalker class. CDriveWalker must know what CDrives knows so that it will be able to walk the collection. The CDrives class must make this information available by providing a friend property:
‘ Private version of data structure
Private af As Long
Private Sub Class_Initialize()
‘ Initialize internal data
af = GetLogicalDrives()
End Sub
‘ Friend properties to make data accessible to data walker class
Friend Property Get DriveFlags() As Long
DriveFlags = af
End Property
The only thing you need to determine all the drives in the system is a bit flag variable (an array of bits) returned by the GetLogicalDrives API function. The CDrives class uses this variable to calculate the number of drives in the collection:
Public Property Get Count() As Integer
Dim c As Long, i As Long
For i = 0 To 25
If MBytes.RShiftDWord(af, i) And &H1 Then c = c + 1
Next
Count = c
End Property
The CDriveWalker class will also need this variable, but it must get it indirectly through the DriveFlags friend property. Unfortunately, this makes the DriveFlags property available to every other class, form, and standard module in the project. Because the project is the VBCore component, that’s a whole lot of chances to make a mistake. Of course, you and I would never use a friend property in a class that wasn’t designed to be a friend, but for those less careful coders who need more than convention to make them behave, it might be nice if the language let us specify who our friends are.
CDrives is an unusual collection class in that it can share all its internal data in one bitflag variable. A more common scenario is that the collection class wraps some other kind of data structure such as an array, a vector, or a linked-list. In that case, the collection must share whatever internal variable makes the data structure work. For example, it might share an array through an indexed property (as described in “Property Arrays,” page 184).
The property that makes CDrives a collection is the NewEnum method. We’ve already seen one NewEnum method on page 205, but that one is simply delegated to the _NewEnum method of the Collection class. We didn’t know or care how the Collection class created its iterator. But this time we’ll need to create our own iterator in the NewEnum method. This is where the real magic begins:
‘ NewEnum must have the procedure ID -4 in Procedure Attributes dialog
‘ Create a new data walker object and connect to it
Public Function NewEnum() As IEnumVARIANT
‘ Create a new iterator object
Dim drivewalker As CDriveWalker
Set drivewalker = New CDriveWalker
‘ Connect it with collection data
drivewalker.Attach Me
‘ Return it
Set NewEnum = drivewalker.NewEnum
End Function
The first requirement is described in the comment. You must give this procedure the ID -4 in the Procedure Attributes dialog box so that Visual Basic will know that it’s a collection. Otherwise, the first time you try to iterate through the collection with For Each, you’ll get a cryptic error at run time telling you that the object doesn’t support this property or method. What it means is that when you write For Each thing In things, Visual Basic looks for a property or a function with ID -4 on the things collection. We name this function NewEnum out of tradition, but the -4 is what really matters.
NewEnum creates a CDriveWalker object. This object will need to use the DriveFlags property of CDrives, but you can’t use a property unless you have a reference to the object that owns that property. The Friend keyword gives you permission, but it doesn’t automatically give you the connection. In this case, we’re going to make a connection by passing a copy of ourselves (Me represents CDrives) to the Attach method of the CDriveWalker object. We’ll see the other side of this connection in a moment.
Finally, the NewEnum method must return a reference to an IEnumVARIANT interface. When you iterate through your collection using For Each, Visual Basic calls methods on this interface—specifically the Next and Skip methods. If NewEnum doesn’t return an IEnumVARIANT, you’ll see the error message Object not a collection. CDrives’ NewEnum returns the IEnumVARIANT interface it gets from CDriveWalker’s NewEnum property. Now we’re ready for even more magic in CDriveWalker.