如果我有一个IShellFolder
接口指针。我怎样才能获得它的 PIDL?
我可以看到如何枚举它的孩子,我可以看到如何使用它来比较任何两个孩子。但是我怎么能得到它自己的pidl呢?
我问是因为我想知道:
这是 IShellFolder == 另一个 IShellFolder
我可以使用IShellFolder::CompareIDs()
,但我必须拥有两个文件夹的 ID。
如果我有一个IShellFolder
接口指针。我怎样才能获得它的 PIDL?
我可以看到如何枚举它的孩子,我可以看到如何使用它来比较任何两个孩子。但是我怎么能得到它自己的pidl呢?
我问是因为我想知道:
这是 IShellFolder == 另一个 IShellFolder
我可以使用IShellFolder::CompareIDs()
,但我必须拥有两个文件夹的 ID。
Chris 或 Mordechai 在 #1 上所写的内容无论如何都不是重点。问题不在于 shell 命名空间中的对象,而在于具有IShellFolder
接口的对象。拥有一个IShellFolder
接口本身并不意味着在 shell 命名空间中的存在。最初的问题是不正确的,因为它假设具有IShellFolder
接口的对象必须具有“它自己的 PIDL”。
我认为,你能做的最好的事情就是如 Mordechai 所建议的那样:
IPersistFolder2
接口此接口的目的是修复 shell 命名空间中的对象,这反过来又使文件夹可持久化。与其从任何缺少已发布的文档中推断,不如看看微软对IPersistFolder
andIPersistFolder2
接口以及Initialize和GetCurFolder方法的实际说法。最为显着地:
您需要实现此接口,以便可以检索 Shell 文件夹对象的 ITEMIDLIST。
关于#2,恐怕克里斯绝对不正确。没有PIDL当然可以IShellFolder
得到。Chris 为#1 引入的控制面板为#2 提供了一个现成的反例。只需喂给CoCreateInstance。CLSID_ControlPanel
_ 您无需“了解 PIDL”即可获得完全可用的控制面板实例。IID_IShellFolder
在 SHELL32 中实现了一些其他可创建的 shell 文件夹,任何 DLL 都可以设置任意数量的其他文件夹。
我发现您可以查询 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;
};
我忘了提到SHGetIDListFromObject
功能。
它仅在 Windows Vista 和更高版本中可用。它的优点是可以记录下来,尽管很简洁。当然,您可以从我自己的文档中获得更多详细信息。这表明 Microsoft 知道另外两种获取 PIDL 的方法,以获取指向 shell 命名空间中对象的任意接口指针。
Mordachai 的回答可能是正确的,但对我来说,这个查询在两个方面没有意义:
我不相信有已发布的文件说 IShellFolder 只能有一个父级。任何特定的 shell 文件夹可能有多种方法。可以通过“我的电脑”、“开始”菜单以及文件系统中的任意位置创建控制面板来访问控制面板。似乎 shell 团队最初的意图是,给定一个 IShellFolder 实例,对于外部用户来说,它的任意位置碰巧是什么都不重要。
另外,任何实例化 IShellFolder 的应用程序肯定都是从了解 PIDL 开始的。如果您的应用关心 IShellFolder 的路径,那么它已经拥有该信息。你是怎么松开的?(为什么 shell 团队要添加一种方法来帮助应用程序跟踪自己的数据?)
如前所述,控制面板等特殊文件夹可能存在很多问题(我仍然不完全理解),但这是“普通”文件夹的简单解决方案:
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;
}