7

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

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

我问是因为我想知道:

这是 IShellFolder == 另一个 IShellFolder

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

4

5 回答 5

8

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

我认为,你能做的最好的事情就是如 Mordechai 所建议的那样:

  • 看看对象是否也有IPersistFolder2接口

此接口的目的是修复 shell 命名空间中的对象,这反过来又使文件夹可持久化。与其从任何缺少已发布的文档中推断,不如看看微软对IPersistFolderandIPersistFolder2接口以及InitializeGetCurFolder方法的实际说法。最为显着地:

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

关于#2,恐怕克里斯绝对不正确。没有PIDL当然可以IShellFolder得到。Chris 为#1 引入的控制面板为#2 提供了一个现成的反例。只需喂给CoCreateInstanceCLSID_ControlPanel_ 您无需“了解 PIDL”即可获得完全可用的控制面板实例。IID_IShellFolder

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

于 2010-11-14T17:09:15.320 回答
6

我发现您可以查询 IShellFolder 的 IPersistFolder2,它具有 GetCurFolder(),它返回其绝对 PIDL。然后我可以简单地使用桌面的 IShellFolder 来比较 IDs() 来确定它们是否相等。我在查看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;
};
于 2009-11-19T23:34:19.883 回答
5

我忘了提到SHGetIDListFromObject功能。

它仅在 Windows Vista 和更高版本中可用。它的优点是可以记录下来,尽管很简洁。当然,您可以从我自己的文档中获得更多详细信息。这表明 Microsoft 知道另外两种获取 PIDL 的方法,以获取指向 shell 命名空间中对象的任意接口指针。

于 2010-11-16T07:23:02.370 回答
1

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

  1. 我不相信有已发布的文件说 IShellFolder 只能有一个父级。任何特定的 shell 文件夹可能有多种方法。可以通过“我的电脑”、“开始”菜单以及文件系统中的任意位置创建控制面板来访问控制面板。似乎 shell 团队最初的意图是,给定一个 IShellFolder 实例,对于外部用户来说,它的任意位置碰巧是什么都不重要。

  2. 另外,任何实例化 IShellFolder 的应用程序肯定都是从了解 PIDL 开始的。如果您的应用关心 IShellFolder 的路径,那么它已经拥有该信息。你是怎么松开的?(为什么 shell 团队要添加一种方法来帮助应用程序跟踪自己的数据?)

于 2009-11-21T11:13:46.023 回答
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;
}
于 2019-01-07T17:07:34.050 回答