ShellBoost

Item identification (PIDLs)

A PIDL (or an IDL) is a fundamental Windows Shell concept. At a high level, it’s pretty simple, as it’s just a “value” that represent a Shell item in the Shell namespace. It’s a bit like a full file system path for a physical file or folder, except it’s a binary object, it’s not a string.

Note: the names “PIDL” and “IDL” can be used interchangeably. The leading P stands for Pointer, but, for convenience, PIDL generally refers to the IDL itself rather than the pointer to it, especially in .NET where we rarely use pointers. To go further on that subject, Microsoft gives more gory details here Identifying Namespace Objects.

So, just like a full file system path is a list of string separated by the \ (slash) character,  a PIDL is a list (L is for List) of IDs (identifiers) separated by a special PIDL separator (which if you really want to know is two consecutive zero bytes).

One very important requirement of PIDLs is they must be serializable (marshaled as a byte array somehow), so they can be persisted, and sent to other machines. This is also a feature so they can be stored in Shell Shortcut files (.lnk) and shared by end-users using Windows usual user interface tools for example.

That seems complicated!

As a ShellBoost developer, you generally don’t need to know how PIDLs or IDs are made, allocated or destroyed, since ShellBoost provides two .NET classes that mask that complexity:

ShellItemId : represents an ID, a segment in the list

ShellItemIdList : represents a PIDL or IDL, which is a list of ShellItemId instance.

With ShellBoost, all Shell Items (including folders) are represented by an instance the ShellItem class. To match Windows Shell namespace organization, every ShellItem instance has an ID, which is an instance of the ShellItemId class. In fact, this identifier instance must be specified in the ShellItem class constructor. Here is the most basic constructor of the ShellItem class:

public ShellItem(ShellFolder parent, ShellItemId id)

As we can see, the constructor needs the parent folder, the folder that contains the new Shell Item, and an Id, uniquely representing the item in the folder.

How to create a ShellItemId?

The ShellItemId class has only one constructor:

protected ShellItemId(byte[] data)

As you see, it’s protected, and it takes whatever data you want, represented as a byte array. Fortunately, you probably will never need to create a new class from ShellItemId because ShellBoost provides a list of ready-made classes for most well-known data types:

KeyShellItemId: supports a Guid, sbyte, byte, short, ushort, int, uint, long, ulong, string, IntPtr, and byte[] types for the ID data, but you can use the specialized classes:

GuidKeyShellItemId: uses a Guid value as the ID

SByteKeyShellItemId: uses an sbyte value as the ID

ByteKeyShellItemId: uses a byte value as the ID

Int16KeyShellItemId: uses a short value as the ID

UInt16KeyShellItemId: uses an ushort value as the ID

Int32KeyShellItemId: uses an int value as the ID

UInt32KeyShellItemId: uses an uint value as the ID

Int64KeyShellItemId: uses a long value as the ID

UInt64KeyShellItemId: uses an ulong value as the ID

StringKeyShellItemId: uses a non-null string value as the ID

IntPtrKeyShellItemId: uses an IntPtr value as the ID

UInt64KeyShellItemId: uses an ulong value as the ID

So, for example, if the shell items in a ShellBoost folder come from rows of a table in a relational database, and the primary key of that table is a Guid value, you could use the GuidKeyShellItemId class to represent IDs of shell items.

Good practice

A ShellItemId (or “ID”) is always relative to a parent folder. In fact, it’s only valid in the context of the Shell Folder which has created it.

A ShellItemIdList (or “IDList” or “PIDL”) is often absolute but can also be relative, just like a file path (\mypath1\mypath2\myfile.ext is absolute while mypath2\myfile.ext is relative), an ID is a segment, and an IDList is a concatenation of segments.

Note a PIDL relative to the desktop is the same as an absolute PIDL, since the Desktop folder is the absolute root of the Shell namespace.

Because an item’s (ShellItem or ShellFolder instance) relative ShellItemId is concatenated with owning parent’s ShellItemIdList to form the item’s absolute ShellItemIdList, a ShellItemId for a given item must only contain a unique key in the parent folder, not a full absolute representation of the item. It would work fine but it’s useless and can create PIDLs that take too much byte space. Remember PIDLs are serialized and deserialized all the time, especially int the ShellBoost cross-process architecture context.

For example, this sample code is bad:

public class MyShellItem : ShellItem
{
    // where someFullPath (relative or absolute) would contain a full hierarchy string
    // like "myRoot.myPath1.myPath2.myName"
    public MyShellItem(MyShellFolder parent, string someFullPath)
        : base(parent, new StringKeyShellItemId(someFullPath))
    {
    }
    ...
}

While this one is good:

public class MyShellItem : ShellItem
{
    // where name would only contain a name instead the parent folder
    public MyShellItem(MyShellFolder parent, string name)
        : base(parent, new StringKeyShellItemId(name))
    {
    }
    ...
}