开发者

How to obtain the PIDL of an IShellFolder

If I have an IShellFolder interface pointer. How might I obtain its PIDL?

I can see how to enumerate its children, and I can see how to use it to compare any two children. But how might I get its own pidl?

I ask because I'd like to know:

Is this IShellFolder == Another IShellFolder

I can use IShellFolder::CompareIDs(), but I have 开发者_运维技巧to have the IDs of both folders.


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.


Mordachai's answer might be correct, but to me this query makes no sense on two fronts:

  1. I don't believe there is a published document saying that an IShellFolder can have only one parent. There might be multiple ways to any particular shell folder. the control panel is accessible via My Computer, via the Start Menu, and anywhere in the file system you create a junction point to it. It seems the shell teams oringinal intention was, given an IShellFolder instance, it should not matter to external users what its arbitrary location happened to be.

  2. Plus, any application that instantiates an IShellFolder surely did so FROM a having knowledge of a PIDL. If your app cared about the path to an IShellFolder it already HAD that information. How did you loose it? (And why should the shell team add a method to help apps keep track of their own data?)


As said before there may be lots of issues with special folders like Control Panel (I still don´t understand it fully) but here is a simple solution for "normal" folders:

HRESULT get_pidl(IShellFolder * sf, LPITEMIDLIST * pidl)
{
    if (!sf || !pidl) return E_FAIL;

    wchar_t FolderName[MAX_PATH] = {0};
    STRRET strDispName; 

    sf->GetDisplayNameOf(NULL, SHGDN_FORPARSING, &strDispName); 
    StrRetToBuf(&strDispName, NULL, FolderName, (UINT)MAX_PATH);

    IShellFolder * desktop = nullptr;
    SHGetDesktopFolder(&desktop);

    ULONG cbEaten, atrib = 0;
    HRESULT hr = desktop->ParseDisplayName(NULL, nullptr, FolderName, &cbEaten, pidl, &atrib);
    desktop->Release();

    return hr;
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜