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 represents a Shell item in the Shell Namespace. It's a bit like a full filesystem path for a physical file or folder, except that it's a binary object and 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 in which we rarely use pointers. To delve deeper into that subject, Microsoft gives more details here Identifying Namespace Objects.

Just like a full filesystem path is a list of strings separated by the \ (backslash) character, a PIDL is a list (L is for list) of IDs (identifiers) separated by a special PIDL separator (two consecutive zero bytes).

An very important requirement of PIDLs is that they must be serializable (i.e., marshaled as a byte array somehow), so that they can be persisted and sent to other machines. They also should be suitable for storing in Shell shortcut files (.lnk) and for sharing by end users using the usual user interface tools in Windows.

That Seems Complicated!

As a CBFS Shell developer, you generally don't need to know how PIDLs or IDs are made, allocated, or destroyed, because CBFS Shell 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 instances.

With CBFS Shell, all Shell items (including folders) are represented by instances of the ShellItem class. To match the 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. Following is the most basic constructor of the ShellItem class:

public ShellItem(ShellFolder parent, ShellItemId id)

As you can see, the constructor needs the parent folder, the folder that contains the new Shell item, and an ID that uniquely represents the item in the folder.

How to Create a ShellItemId

The ShellItemId class has only one constructor:

protected ShellItemId(byte[] data)

As you can see, this item 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 CBFS Shell provides a list of ready-made classes for the 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 a ushort value as the ID
  • Int32KeyShellItemId: uses an int value as the ID
  • UInt32KeyShellItemId: uses a uint value as the ID
  • Int64KeyShellItemId: uses a long value as the ID
  • UInt64KeyShellItemId: uses a ulong value as the ID
  • StringKeyShellItemId: uses a non-null string value as the ID
  • IntPtrKeyShellItemId: uses an IntPtr value as the ID

For example, if shell items in a CBFS Shell 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 the IDs of the Shell items.

Good Practice

A ShellItemId (or ID) is always relative to a parent folder. In fact, it is valid only in the context of the Shell folder that has created it.

A ShellItemIdList (or IDList or PIDL) is often absolute but also can 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, because the Desktop folder is the absolute root of the Shell Namespace.

Because an item's (ShellItem or ShellFolder instance) relative ShellItemId is concatenated with the owning parent's ShellItemIdList to form the item's absolute ShellItemIdList, a ShellItemId for a given item should contain only a unique key in the parent folder, not a full absolute representation of the item. The latter variant is technically possible but very inefficient. Remember that PIDLs are serialized and deserialized all the time, especially in the CBFS Shell cross-process architecture context.

For example, the following sample code is inefficient:

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))
    {
    }
    ...
}

Compare the inefficient sample code with the following sample code, which is preferable:

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))
    {
    }
    ...
}

Copyright (c) 2022 Callback Technologies, Inc. - All rights reserved.
CBFS Shell 2022 .NET Edition - Version 22.0 [Build 8367]