11.2 Explorer Architecture

only for RuBoard - do not distribute or recompile

11.2 Explorer Architecture

As Figure 11.4 illustrates, there are five distinct parts to Explorer: the menu, the toolbar, the tree view, the content pane (or view), and the status bar.

Figure 11.4. Explorer architecture
figs/vshl.1104.gif

When Explorer finds a namespace extension at a junction point (as defined in any of the four ways discussed in Section 11.1.2 earlier in this chapter), it loads the extension and queries for IShellFolder . This interface represents the folder in the tree view and basically acts as a liaison to the rest of the namespace extension. Everything a namespace extension needs is generated through this interface.

Explorer then asks the extension for an IShellView interface. This interface is provided by IShellFolder and is responsible for creating the view window in the content pane. The view in turn is responsible for displaying the data. Something that might not be so obvious is that Explorer does not provide the list view that is usually found in the content pane. It is the responsibility of the object implementing IShellView to create this window. Also, it must be noted that a namespace extension must be prepared to create multiple views. For example, Explorer provides five views: Large Icons, Small Icons, Details, List, and View as Web Page. But you can also provide custom views. Consider the Fonts namespace extension. It provides a view called List Fonts by Similarity that allows you to see groups of fonts that are similar in appearance. Because several views are possible, the object implementing IShellFolder is distinct from the object implementing IShellView . This is a one-to-many relationship.

Explorer provides the object implementing IShellView with a reference to an interface called IShellBrowser . This interface can be used by the view object to manipulate the menu, toolbar, and status bar of Explorer to add new menu items and toolbar buttons , and to manage text in the status bar.

Once the content pane has been made ready to receive data, the shell asks the namespace extension to enumerate the contents of the folder. This is handled by a third object that implements the IEnumIDList interface. This object is separate from IShellFolder because, like IShellView , it must be called multiple times throughout the lifetime of the extension.

Every time a branch of the extension is opened, an instance of IEnumIDList is created. In the tree view, the enumerated items that have the "folder" attribute are displayed. If these folders have the "has subfolders " attribute, a "+" node is displayed. The "+" nodes, of course, can be opened, and the entire process begins again.

IShellFolder provides services to handle the display text for each item. Additional user interface elements such as icons, context menus , and InfoTips are provided for as well.

only for RuBoard - do not distribute or recompile
only for RuBoard - do not distribute or recompile

11.3 The PIDL

Explorer needs a way to uniquely identify each item in the namespace unambiguously in relation to other items in the namespace. It must be able to enumerate these items in a consistent, generic manner, even though these items represent a wide variety of data. It does this with a PIDL.

A PIDL is a pointer to an item identifier list, or ITEMIDLIST . An ITEMIDLIST is an array of shell item IDs. Each one of these identifiers is an array of bytes that contains information that is specific to the namespace extension using it.

How can Explorer use PIDLs if they are different in respect to every extension? Well, as it turns out PIDLs are pretty simple creatures . Let's look at how a PIDL is defined, and you'll be able to see this for yourself. Here is what an ITEMIDLIST (just remember a PIDL is a pointer to one of these) looks like, as defined by the Platform SDK:

 typedef struct _ITEMIDLIST {
    SHITEMID mkid;
} ITEMIDLIST, * LPITEMIDLIST; 

As you can see, an ITEMIDLIST is nothing more than a structure that contains one member of type SHITEMID . This structure looks like so:

 typedef struct _SHITEMID { 
    USHORT cb; 
    BYTE abID[1]; 
} SHITEMID, * LPSHITEMID; 

The first member of SHITEMID , cb , contains the number of bytes of the SHITEMID structure. SHITEMID is a variable-length structure, and cb contains two bytes specifying its size . For those of you who have never done any C programming, you probably have never seen this technique before: the first member of a structure is used to define the total length of a variable-length structure. The abID parameter is not a pointer, of course, because it is only 1 byte. It is a placeholder. It marks the first byte of an unknown number of bytes. One member of a variable length structure contains the number of bytes that begins at the location abID[0] . This is an efficient way to maintain a collection (a linked list perhaps) of like structures that are of different sizes. Without this technique, you would have to reallocate memory like this:

 typedef struct _SHITEMID { 
    USHORT cb; 
    BYTE abID[1024]; 
} SHITEMID, * LPSHITEMID; 

This is very inefficient, because you may have one instance of this structure that contains 1024 bytes of data and several hundred that contain only 10 bytes. Also, you are limited as to how large your structure can be. Because SHITEMID is of variable length, it can be as large or as small as needed.

Anyway, the data that follows can be in any format that is required by the namespace extension. Well, almost, but we'll talk about that in a second. This format allows IEnumIDList to enumerate a list of PIDLs in a generic fashion.

A PIDL is just a pointer to one or more ITEMIDLIST s that is terminated by an empty ITEMIDLIST (two 0s). Some PIDLs point to only one ITEMIDLIST, followed by an empty ITEMIDLIST . These are called simple PIDLs. A PIDL that points to more than one ITEMIDLIST is called a complex PIDL. Regardless of the type, the last ITEMIDLIST must contain all NULL values. This is shown in Figure 11.5.

Figure 11.5. Simple and complex PIDLs
figs/vshl.1105.gif

A PIDL always points to at least two ITEMIDLIST s (remember, there's always an empty ITEMIDLIST at the end). In other words, it's an array. So if you read something like "the last item in the PIDL," this means the last ITEMIDLIST in the array. So don't just think of a PIDL as a pointer to an ITEMIDLIST . It's easier to think about it in terms of an array, because that's what it really is. There is never just one ITEMIDLIST . Also, not to make things more confusing, but, by convention, the term PIDL is often used when referring to the underlying ITEMIDLIST structure. For instance, you'll never hear someone say, "What is the format of your ITEMIDLIST ?" They'll just call it a PIDL. So when you read about the "format of a PIDL," you now know that what is being discussed is the ITEMIDLIST itself. With that said, let's talk about the format of a PIDL.

There is an important rule that must be followed when creating a PIDL. The data contained in your PIDLs ( ITEMIDLIST s) cannot contain pointers . This is because PIDLs can be persisted (saved to disk) and then read back into memory at some point in the future (shortcut files are persisted ID lists). ITEMIDLIST s can also be copied into another memory block before they are used by Explorer. So a PIDL cannot contain a handle to an icon, for example; it must contain all the actual bits that make up the icon. A PIDL can't contain a pointer to a path, it must contain the actual path itself. Everything a PIDL needs to describe itself must be contained within it.

only for RuBoard - do not distribute or recompile