The CDrives collection, version 1


You can use the CDrive class from Chapter 3 to create a CDrives collection class. Notice the naming convention: collectionlike classes should always be plural. Figure 4-2 shows the CDrives collection and its relationship to the CDrive class. But before we get to the real CDrives class, let’s look at my first implementation, which I saved under the name CDrivesO.



Figure 4-2. The CDrives class.


The following code (from TCOLLECT.FRM) shows that CDrives and CDrivesO are mostly polymorphic classes that can be called using the same code:

s = s & “Drive information for available drives:” & sCrLf
Dim drives As Object, drive As CDrive
If chkOld Then
Set drives = New CDrivesO
Else
Set drives = New CDrives
End If
For Each drive In drives
With drive
s = s & “Drive “ & .Root & “ [“ & .Label & “:” & _
.Serial & “] (“ & .KindStr & “) has “ & _
Format$(.FreeBytes, sBFormat) & “ free from “ & _
Format$(.TotalBytes, sBFormat) & sCrLf
End With
Next

Most of the work in a class representing an internal Collection is done in the Class_Initialize event procedure. Here’s how it works in CDrivesO:

Private drives As New Collection

Private Sub Class_Initialize()
Refresh
End Sub ‘ Argument handy for refreshing local and/or remote, but not floppies
Public Sub Refresh(Optional iFirst As Integer = 1)
Dim i As Integer, af As Long, sRoot As String
Dim drive As CDrive
‘ Remove old ones
Do While drives.Count > iFirst
drives.Remove iFirst
Loop
‘ Insert new
af = GetLogicalDrives()
For i = iFirst To 26
If RShiftDWord(af, i - 1) And 1 Then
Set drive = New CDrive
drive.Root = i
drives.Add drive, drive.Root
End If
Next
End Sub

The Class_Initialize Sub simply calls Refresh to do the real work. Calling a public initialization method from the initialization event is a common technique in classes that sometimes need to override automatic initialization. Refresh uses
the Win32 GetLogicalDrives function and the RShiftDWord function (described in Chapter 5) to calculate which drives actually exist in the system. The drives are then initialized and added to the internal Collection. The Refresh method is important and useful for the old version, but you’ll soon see that it’s now history in the real CDrives class, which calculates drives as they are needed.


With the internal Collection in place, it’s easy to implement the standard properties and methods of a collection. You expose the Count and Item properties by passing through the Count and Item of the internal Collection as follows:

Public Property Get Count() As Integer
Count = drives.Count
End Property

‘ Default property
Public Property Get Item(v As Variant) As CDrive
‘ Return default (Nothing) if error
On Error Resume Next
Set Item = drives(v)
End Property

Embedding a Collection object in the class and passing its members through with similar external members is another example of delegation. In a classic object-oriented language, you would use inheritance for the same purpose. Specifically, you would derive the CDrivesO class from the Collection class. You wouldn’t have to write any code to get the Count and Item properties. You would have to write code for any additional members you wanted (such as Refresh), and you’d disable or enhance any members you wanted to eliminate or change (such as Add and Remove).

At first glance, you might think it impossible to implement the Add and Remove properties. If you could add a new drive to your system just by calling the Add method, you’d never run out of disk space, but even the Plug and Play standard can’t promise that. On the other hand, it’s easy enough to use Add to connect to a network drive and Remove to disconnect one. Check out the WNetAddConnection2 and WNetCancelConnection2 API functions. I’ll leave it to you to enhance the CDrives collection to make it fully network-aware.