How to obtain the PIDL of an IShellFolder

What either Chris or Mordechai writes on #1 is anyway not to the point. The question is not about objects in the shell namespace but about objects that have an IShellFolder interface. Possession of an IShellFolder interface does not itself imply a presence in the shell namespace. The original question is ill-formed, inasmuch as it assumes that an object with an IShellFolder interface must have "its own PIDL".

The best you can do, I think, is as Mordechai suggests:

  • see if the object also has an IPersistFolder2 interface

The purpose of this interface is to fix the object in the shell namespace, which is in turn what makes the folder persistable. Rather than infer from any absence of published documentation, look at what Microsoft actually does say of the IPersistFolder and IPersistFolder2 interfaces and the Initialize and GetCurFolder methods. Most notably:

you need to implement this interface so that the Shell folder object's ITEMIDLIST can be retrieved.

On #2, I'm afraid Chris is definitely not correct. An IShellFolder certainly can be obtained without a PIDL. The Control Panel, which Chris introduced for #1, provides a ready counter-example for #2. Just feed CLSID_ControlPanel and IID_IShellFolder to CoCreateInstance. You get a perfectly usable instantiation of the Control Panel without ever "having knowledge of a PIDL".

There are a handful of other creatable shell folders implemented in SHELL32, and any DLL can set up any number of others.


I found that you can query an IShellFolder for its IPersistFolder2, which has GetCurFolder(), which returns its absolute PIDL. I could then simply use the IShellFolder for the desktop to CompareIDs() to determine if they're equal. I found the outlines of this while looking at SHGetIDListFromObject. I couldn't just use that function, because its Vista, and I need XP compatibility.

Here's a sketch of how it works (assuming you have an ifolder_desktop, and ifolder_other, which are IShellFolder pointers. Pidl is a simple helper that ensures that the IDLISTs are deallocated properly):

CComQIPtr<IPersistFolder2> ipf2_desktop(ifolder_desktop);
CComQIPtr<IPersistFolder2> ipf2_folder(ifolder_other);

Pidl pidl_desktop, pidl_folder;
VERIFY(SUCCEEDED(ipf2_desktop->GetCurFolder(pidl_desktop)));
VERIFY(SUCCEEDED(ipf2_folder->GetCurFolder(pidl_folder)));

HRESULT hr = ifolder_desktop->CompareIDs(NULL, pidl_desktop, pidl_folder);
pCmdUI->Enable(SUCCEEDED(hr) && HRESULT_CODE(hr) != 0);

In case anyone is interested in my simple Pidl class:

class Pidl
{
public:
    // create empty
    Pidl() : m_pidl(NULL) { }

    // create one of specified size
    explicit Pidl(size_t size) : m_pidl(Pidl_Create(size)) {}

    // create a copy of a given PIDL
    explicit Pidl(const ITEMIDLIST * pidl) : m_pidl(Pidl_Copy(pidl)) {}

    // create an absolute PIDL from a parent + child
    Pidl(const ITEMIDLIST_ABSOLUTE * pParent, const ITEMIDLIST_RELATIVE * pChild) : m_pidl(Pidl_Concatenate(pParent, pChild)) { }

    // return our PIDL for general use (but retain ownership of it)
    operator const ITEMIDLIST * () { return m_pidl; }

    // return a pointer to our pointer, for use in functions that assign to a PIDL
    operator ITEMIDLIST ** () 
    {
        free();
        return &m_pidl; 
    }

    // release ownership of our PIDL
    ITEMIDLIST * release() 
    { 
        ITEMIDLIST * pidl = m_pidl;
        m_pidl = NULL;
        return pidl;
    }

    void free()
    {
        if (m_pidl)
            //Pidl_Free(m_pidl);
            ILFree(m_pidl);
    }

    // automatically free our pidl (if we have one)
    ~Pidl()
    {
        free();
    }

private:
    ITEMIDLIST * m_pidl;
};

I forgot to mention the SHGetIDListFromObject function.

It's only available in Windows Vista and higher. It has the advantage of being documented, albeit tersely. You get more detail, of course, from my own documentation. This shows that Microsoft knows two more ways of getting a PIDL for an arbitrary interface pointer to an object in the shell namespace.