为啥控制面板的 PIDL 会不同?

Posted

技术标签:

【中文标题】为啥控制面板的 PIDL 会不同?【英文标题】:Why would the PIDL for the control panel be different?为什么控制面板的 PIDL 会不同? 【发布时间】:2012-12-28 20:32:48 【问题描述】:

我看到应该使用this 来比较 PIDL:IShellFolder::CompareIDs()。

特别是,我试图检测给定的绝对 PIDL(或相对)是否是控制面板的。

但是,在实践中,我最终得到了两个 IShellFolder::CompareIDs() 声称不相等的 PIDL,当它们应该相等时(查看每个的 GetDisplayName(),我可以看到我们确实在查看控制面板)。

基本上,我通过以下方式获取控制面板的绝对 PIDL:

PIDL iidControlPanel = nullptr;
SHGetSpecialFolderLocation(hwnd, CSIDL_CONTROLS, &iidControlPanel);

然后像这样比较传入的枚举 shell 对象(有关上下文,请参阅 here - 简而言之,这是查看枚举 CMFCShellTreeCtrl 内的桌面 shell 命名空间的结果):

bool bIsControlPanel = CompareAbsolutePIDLs(iidControlPanel, pItem->pidlFQ);

作为参考,这里是比较函数:

bool CompareAbsolutePIDLs(PIDLIST_ABSOLUTE pidl1, PIDLIST_ABSOLUTE pidl2)

    CComPtr<IShellFolder> ishDesk;
    SHGetDesktopFolder(&ishDesk);
    HRESULT hr = ishDesk->CompareIDs(SHCIDS_CANONICALONLY, pidl1, pidl2);
    return SUCCEEDED(hr) && HRESULT_CODE(hr) == 0;

在调试器中,我可以看到每个返回的 GetDisplayName():

"::26EE0668-A00A-44D7-9371-BEB064C98683\0"
"::26EE0668-A00A-44D7-9371-BEB064C98683"

在这里您可以看到 PIDL 的十六进制转储:

1f 70 68 06 ee 26 0a a0 d7 44 93 71 be b0 64 c9 86 83 *0c* 00
1f 70 68 06 ee 26 0a a0 d7 44 93 71 be b0 64 c9 86 83 *00* 00

除了倒数第二个值(00 与 0c)外,底层 PIDLS 也是二进制相同的。我目前不知道为什么它们不同,或者我能做些什么来解决这个问题?!

问题

    是否有另一种方法可以不包含看似虚假的额外空字节的方式获取控件的 PIDL? 或者,是否有更好的方法来获取枚举项的 PIDL(CMFCShellTreeCtrol 获取绝对 PIDL 的方式是否存在不足,以至于它无法包含最终的空字节?) 有没有办法将控制面板获取为相对 PIDL,然后将其与相对枚举 PIDL(我也有)进行比较? ???

【问题讨论】:

我怀疑一个 PIDL 用于“标准”控制面板,另一个用于“所有控制面板项目”文件夹(也称为“上帝模式”)。既然GetDisplayName 为两者返回一致的 GUID,为什么不使用它并比较字符串而不是尝试比较 PIDL? 这是一个 hack,但我可能不得不这样做。我怀疑这个 PIDL 和显示名称可能会因操作系统版本而异——因此我可能必须在 XP Vista、7 和 8 上进行试验以确定所有可能的命名。 :( FWIW 我在自己的代码中做了类似的事情,发现我必须与三个不同的 GUID 进行比较:CLSID_ControlPanel(它位于系统标题中,是您在 XP 上可以获得的全部内容), 26EE0668-A00A-44D7-9371-BEB064C98683(Vista 及以上)和 5399E694-6CE5-4D6C-8FCE-1D8870FDCBA0(Win7 及以上)。此外,在 Win7 及更高版本下,注册表可能在 HKCR\CLSID\guid 下包含一个“System.ControlPanel.Category”值,表示控制面板类别文件夹 GUID,您可能也想对此进行测试。 您是否尝试过传递 0 而不是 SHCIDS_CANONICALONLY? 【参考方案1】:

这些确实是不同的 shell 对象。您可以使用 SIGDN_NORMALDISPLAY 选项将获得的 PIDL 传递给 SHGetNameFromIDList() 以将它们转换为可读字符串。长的 PIDL(带 0x0c)转换为“所有控制面板项”,短的 PIDL 转换为“控制面板”。

这个问题是通过使用 SHGetSpecialFolderLocation() 来检索控制面板控件的虚拟文件夹开始的。与桌面根目录下的控制面板对象不同。我认为您需要通过获取控制面板的 PIDL 并忽略虚拟文件夹来解决此问题。一种方法是使用 ILCloneFirst 将虚拟文件夹转换为根对象:

 PITEMID_CHILD controlPanel = ILCloneFirst(iidControlPanel);

或者您可以硬编码控制面板 CLSID“::26EE0668-A00A-44D7-9371-BEB064C98683”,然后使用 SHParseDisplayName() 将其转换为 PIDL。

【讨论】:

谢谢汉斯!我仍在试图围绕虚拟文件夹和外壳对象。这有帮助。 :)

以上是关于为啥控制面板的 PIDL 会不同?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 mysql 数据库在 xampp 状态下被停用但在 xampp 控制面板应用程序上呈绿色?

Windows服务已经打包好了。为啥安装后,在电脑控制面板-服务里没有看到

为啥更新不同的 TextBox.Text 时面板会自动向上滚动?

18款开源/商用的Linux服务器控制面板

MS Access 2003/2007 - 子窗体作为控制面板,关闭父窗体与全局类似参考?

amh面板安装后,无法进去amh页面是为啥