Doing something with folders


After all those preliminaries, we’re ready to actually do something. Let’s start with the Test Shell Folders program (TFOLDER.VBG) shown in Figure 11-11. It can walk through files or folders, one at a time or all at once. You can start with either a path or one of the locations shown in the Special Folders list box. You can click the Context Menu button to bring up a context menu on the current path or special folder, and then you can click the button with three dots to browse for a starting folder. And as a bonus, you can stop a walk. You’ll see what this button is for if you try to walk the whole desktop.


Let’s start with the Walk All Folders button:

Private Sub cmdWalkFolders_Click()
Dim folder As IVBShellFolder
txtOut = “Walk folders recursively: “ & sCrLfCrLf
fStop = False
fWalkAll = True
If chkPath Then
Set folder = FolderFromItem(txtPath)
Else
With lstSpecial
Set folder = FolderFromItem(.ItemData(.ListIndex))
End With
End If
WalkAllFolders folder, Me, 0, WalkType(cboWalk.ListIndex)
End Sub

The event procedure uses FolderFromItem to turn a special folder constant or a path into a folder. Once you have a folder, you can start walking. The form implements the IUseFolder interface, which WalkAllFolders will call back in the same way that WalkAllFiles calls IUseFile. The Me argument represents the form’s IUseFolder object. And finally, after several pages of preliminaries, we’re ready to do some actual work. I don’t think WalkAllFolders will be an anticlimax:

Function WalkAllFolders(folder As IVBShellFolder, foldit As IUseFolder, _
Optional ByVal Level As Long = 0, _
Optional ByVal ewm As EWalkMode = ewmBoth, _
Optional ByVal hWnd As Long = hNull) As Long
InitIf ‘ Initialize if in standard module

Dim idenum As IVBEnumIDList, folderNew As IVBShellFolder
Dim pidl As Long, cFetched As Long, afAttrib As Long

‘ Get the IEnumIDList object for the given folder
On Error GoTo WalkAllFoldersFail
folder.EnumObjects hWnd, ewm, idenum

‘ Enumerate through the list of folder and nonfolder objects
On Error GoTo WalkAllFoldersFail2
Dim hRes As Long
Do
hRes = idenum.Next(1, pidl, cFetched)
‘ 0 means got another, 1 means no more, anything else is error
‘ but there had better not be any errors because we’ll ignore them
If hRes Then Exit Do

‘ Pass to user-implemented interface to do something with folder
‘ (True in return means user requested termination)
WalkAllFolders = foldit.UseFolder(Level, folder, pidl)
If WalkAllFolders Then
Allocator.Free pidl
Exit Function
End If

‘ It’s not in the docs, but you pass in the attributes you want
‘ to check and GetAttributes passes back whether those attributes
‘ are set, ignoring all others
afAttrib = SFGAO_HASSUBFOLDER Or SFGAO_FOLDER
folder.GetAttributesOf 1, pidl, afAttrib

‘ If there are subfolders, process them recursively
If afAttrib And (SFGAO_HASSUBFOLDER Or SFGAO_FOLDER) Then
folder.BindToObject pidl, 0, iidShellFolder, folderNew
WalkAllFolders = WalkAllFolders(folderNew, foldit, Level + 1, ewm)
End If
WalkAllFoldersFail2:
‘ Free the pidl from Next
Allocator.Free pidl
Loop
WalkAllFoldersFail:

End Function

The walk is accomplished through a call to the EnumObjects method of IShell­Folder. It returns an IEnumIDList object, which looks and works a lot like the IEnumVARIANT interface described in Chapter 4. Of course, as we’ve noted several times (especially in Chapters 3, 4, and 10), most system interfaces are Basic-hostile, forcing us to use Basic-friendly binary-compatible versions called IVB­ShellFolder and IVBEnumIDList.


Once we’ve created an ID enumerator, we can call its Next method to walk through all the items in the folder. For each item, we call the UseFolder method of IUseFolder to let the client do whatever they want with the folder. The Use­Folder method takes a CFileInfo parameter, providing the client with all the information they could possibly want. You can check out my FileInfoFromFolder function to see how a folder and a PIDL become a CFileInfo object. If the user doesn’t tell us to stop (by returning True), we call the GetAttributesOf method to see whether this folder has subfolders. If it does, we call the BindToObject method to bind the current folder object to a new sub­folder object. Finally we call ourselves recursively to handle the new folder.


Whew!


And that’s just the start. You might also want to check out ContextPopMenu. It starts out similar to WalkAllFolders, but instead of binding to a nested folder, it calls the GetUIObjectOf method to get an IVBContextMenu object and then uses that object to display the standard context menu for the item. ContextPopMenu is based on a C version in a PC Magazine article by Jeff Prosise.


Ideally, I’d provide other functions or classes that integrate other shell features such as property sheet handlers, icon extractors, and drag and drop handlers. Another time.