Folders and items


You can compare folders and items to the registry. In fact, I probably should have written CShellNode and CShellItem classes that work the same as the CRegNode and CRegItem classes described in Chapter 10. Go ahead. I dare you. It would be considerably more difficult because the shell name space is even more anarchical than the registry.


Shell items, for example, have a binary format that can be defined and known only by their designer. You aren’t supposed to know anything about an item except that its first two bytes contain the number of bytes in the item. Items are arranged in lists which are terminated with a null item consisting of nothing more than two bytes containing a count of zero. This is the kind of data structure that would normally be managed through a handle and some functions that operate on the handle. But there are no handles for items. All you get is a pointer to a list of items. This is the infamous PIDL—which as far as I can tell stands for Pointer to ID List. The official name of an item is an Item ID, and the C data structure that represents it is called a SHITEMID. I think the SH stands for shell. In any case, programmers are supposed to write their own functions to manage PIDLs, but you don’t have to because I did it for you. Generally, you can just think of a PIDL as a kind of handle and use the following functions from FOLDTOOL.BAS to manipulate them:

Function Purpose
ItemIDSize(ByVal pidl As Long) As Integer Gets the byte size of an ID list
PidlCount(ByVal pidl As Long) As Long Counts the item IDs in an item ID list
NextItemID(ByVal pidl As Long) As Long Gets the next item ID in an item ID list
DuplicateItemID(pidl As Long) As Long Duplicates an item ID (creator must free)
DuplicateItemIDs(ByVal pidl1 As Long, _ Concatenates two item Ids ByVal pidl2 As Long) As Long
PathFromPidl(ByVal pidl As Long) As String Converts a PIDL to a file system path
PidlFromPath(sPath As String) As Long Converts a file system path to a PIDL
ToPidl(ByVal i As Long) As Long Converts an integer representing a special folder location constant to a PIDL. This function also accepts a PIDL and passes it through unchanged


Table 11-2. Shell folder functions.


I’m not going to say much more about these functions, but they will be used (directly or indirectly) in the next few sections. There’s one other unusual thing about the PIDLs; you must free them with the system allocator. The creator of an item ID (Windows or the designer of a shell extension) uses the system allocator to allocate memory for the item. It’s your responsibility to track that item and free it when you’re finished with it. This is worse than un-Basic; it’s anti-Basic. Our language is supposed to clean up memory for us behind the scenes. Ideally, someone would come up with a way of encapsulating PIDLs so that they would always be cleaned up automatically. In the meantime, we must free the memory ourselves, just as C programmers do for almost all their data structures. I did manage to encapsulate the system allocator in VBCore. Just use the global Allocator object to free your PIDLs:

Allocator.Free pidl

Moving on raPIDLy, we also need a few tools to manage folders. The most important folder is the one at the top of the hierarchy—the Desktop folder. You can get it with the GetDeskTopFolder function (from VBCore):

Set folder = GetDeskTopFolder

You can also turn a file system directory into a folder, using a procedure so bizarre that you’ll have to look it up to figure it out. The FolderFromItem function turns what I call an item into a folder, using many of the utility functions shown earlier. It can also return a PIDL representing an item in the folder. Often you’ll need both a folder and a PIDL within the folder to do anything useful. (I’ll get to the useful part soon.) An item in this context is actually a Variant that can take a file system path string, a PIDL, or a constant representing a special folder location. Special folder locations include the Desktop, Printers, My Computer, and so on. We saw special folders in the TWHIZ project described
earlier in this chapter. Anyway, to convert an item, you call FolderFromItem
like this:

Dim pidlOut As Long
Set folder = FolderFromItem("C:\Hard\Core", pidlOut)
' Use the folder
§
' Free the PIDL when done
Allocator.Free pidl

That’s the path version. The PIDL and special folder versions are similar:

Set folder = FolderFromItem(pidlIn, pidlOut)
Set folder = FolderFromItem(CSIDL_BITBUCKET, pidlOut)

And now for one last utility function. Once you’ve got a folder and a PIDL, you might want an intelligible name for the item. You can get the name from the GetDisplayNameOf method of the IShellFolder interface. Unfortunately, this method returns one of the most bizarre data types ever devised—what windows calls STRRET and what I call the type from hell. It represents the returned string in one of three different formats, each of which presents a new challenge for the hardcore Visual Basic programmer. Fortunately, you don’t have to worry about it because I already did your crashing for you. Just call the GetFolderName function like this:

sName = GetFolderName(folder, pidl, SHGDN_FORPARSING)

The last parameter is an Enum that will give you either a full path or a more attractive format (SHGDN_FORPARSING or SHGDN_NORMAL, respectively). There are a couple of other values that give minor variants in display format.