如何获取IShellFolder的PIDL

时间:2009-11-19 20:26:41

标签: c++ winapi com windows-shell

如果我有一个IShellFolder接口指针。我怎样才能获得它的PIDL?

我可以看到如何枚举它的孩子,我可以看到如何使用它来比较任何两个孩子。但是我怎么能得到它自己的pidl?

我问,因为我想知道:

  

这是IShellFolder ==另一个IShellFolder

我可以使用IShellFolder::CompareIDs(),但我必须拥有两个文件夹的ID。

5 个答案:

答案 0 :(得分:7)

Chris或Mordechai在#1上所写的内容无论如何都不是重点。问题不是关于shell命名空间中的对象,而是关于具有IShellFolder接口的对象。拥有IShellFolder接口本身并不意味着存在于shell命名空间中。原始问题是不正确的,因为它假定具有IShellFolder接口的对象必须具有“其自己的PIDL”

我认为,你能做的最好的事情就像莫迪凯建议的那样:

  • 查看对象是否还有IPersistFolder2界面

此接口的目的是将对象固定在shell命名空间中,这又使文件夹可以保持不变。不要推断任何已发布文档的缺失,而是看看Microsoft实际上对IPersistFolderIPersistFolder2接口以及初始化 GetCurFolder 的说法方法。最值得注意的是:

  

您需要实现此接口,以便可以检索Shell文件夹对象的ITEMIDLIST。

在#2上,我担心克里斯绝对不正确。当然可以在没有PIDL的情况下获得IShellFolder。 Chris为#1引入的控制面板为#2提供了一个现成的反例。只需将CLSID_ControlPanelIID_IShellFolder提供给 CoCreateInstance 即可。您可以获得一个完全可用的控制面板实例,而无需“了解PIDL”

在SHELL32中实现了一些其他可创建的shell文件夹,任何DLL都可以设置任意数量的其他文件夹。

答案 1 :(得分:6)

我发现您可以在IShellFolder中查询其IPersistFolder2,它具有GetCurFolder(),它返回其绝对PIDL。然后,我可以简单地使用桌面的IShellFolder来CompareIDs()来确定它们是否相等。我在查看SHGetIDListFromObject的同时找到了这个概述。我不能只使用那个功能,因为它的Vista,我需要兼容XP。

这是一个如何工作的草图(假设你有一个ifolder_desktop和ifolder_other,它们是IShellFolder指针.Pidl是一个简单的帮助器,可以确保正确释放IDLIST):

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);

如果有人对我的简单Pidl课感兴趣:

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;
};

答案 2 :(得分:5)

我忘了提及SHGetIDListFromObject功能。

仅适用于Windows Vista及更高版本。它具有记录的优点,尽管很简洁。当然,您可以从my own documentation获得更多详细信息。这表明Microsoft知道另外两种获取PIDL的方法,该方法指向shell命名空间中对象的任意接口指针。

答案 3 :(得分:1)

Mordachai的回答可能是正确的,但对我来说,这个问题在两个方面都没有意义:

  1. 我不相信有一个已发布的文档说IShellFolder只能有一个父级。任何特定的shell文件夹可能有多种方法。控制面板可通过“我的电脑”,“开始”菜单以及您创建的文件系统中的任何位置访问 结点指向它。看来shell团队的oringinal意图是,给定一个IShellFolder实例,外部用户它的任意位置恰好是什么

  2. 另外,任何实例化IShellFolder 肯定的应用程序都是在了解PIDL的情况下完成的。如果你的应用程序关心IShellFolder的路径,它已经知道了这些信息。你是怎么放松的? (为什么shell团队会添加一种方法来帮助应用程序跟踪他们自己的数据?)

答案 4 :(得分:0)

如前所述,诸如“控制面板”之类的特殊文件夹可能存在很多问题(我仍然不太了解),但这是“普通”文件夹的简单解决方案:

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;
}