给定Windows Vista或更高版本的 IShellItem
,我如何获取与该项目关联的系统镜像列表图标索引?
例如(伪代码):
IShellItem networkFolder = SHGetKnownFolderItem(FOLDERID_NetworkFolder, 0, 0, IShellItem);
Int32 iconIndex = GetSmallSysIconIndex(networkFolder);
Int32 GetSmallSysIconIndex(IShellItem item)
{
//TODO: Ask Stackoverflow
}
背景
在过去的时代(Windows 95和更高版本),我们可以要求 shell 程序为我们提供项目图标的系统imagelist索引。我们使用
SHGetFileInfo
做到了。 SHGetFileInfo函数gets the icon by asking the shell namespace for the icon index in the system imagelist:HICON GetIconIndex(PCTSTR pszFile)
{
SHFILEINFO sfi;
HIMAGELIST himl = SHGetFileInfo(pszFile, 0, &sfi, sizeof(sfi), SHGFI_SYSICONINDEX));
if (himl) {
return sfi.iIcon;
} else {
return -1;
}
}
当您在 shell namespace 中使用与文件相对应的项目时,此方法有效。但是 shell 程序支持文件系统中文件和文件夹之外的其他内容。
IShellFolder的图标索引
获取有关 shell namespace 中对象的信息的一般解决方案来自使用您在 shell namespace 中表示项目的方式:
IShellFolder
:事物所在的文件夹以及PIDL
:该文件夹中事物的ID 从那里有ways to get the system image list index:
Int32 GetIconIndex(IShellFolder folder, PITEMID_CHILD childPidl)
{
//Note: I actually have no idea how to do this.
}
但是IShellFolder已经退出;我们现在使用IShellItem
从Windows Vista开始,
IShellItem
成为the preferred AP我用于浏览Shell。 Windows 95时代的API,必须在was cumbersome, and error prone周围保留IShellFolder
+ pidl
对。问题就变成了:如何用它做东西?特别是,如何获得该项目的系统图像列表中的图像索引?看一下它的方法,甚至没有办法得到它的绝对pidl:
我希望可以通过Windows Property System访问的
IShellItem2
具有与 shell imagelist图标索引相关联的属性。不幸的是,我没有看到任何东西:提取图标的方式
Windows Shell命名空间中提供了附带图片的are many standard ways:
IExtractIcon
。返回HICON
。需要IShellFolder
+ pidl。如果失败you can use SHDefExtractIcon SHDefExtractIcon
。返回HICON
。需要图标文件IThumbnailCache
。需要IShellItem
。返回缩略图,而不是图标IShellImageFactory
。获取表示IShellItem
IThumbnailProvider
。 Windows Vista替代IExtractImage
IExtractImage
。需要IShellFolder
+ pidl
。 SHGetFileInfo
。需要完整的文件路径或绝对的pidl 没有一个:
最佳答案
基本上,似乎没有任何简单的方法可以做到这一点。根本没有在API中提供它。
在您的问题中,您说“但是 shell 程序支持文件系统中文件和文件夹之外的内容。”,这使我认为您忽略了SHGetFileInfo
实际上确实支持直接使用PIDL(带有SHGFI_PIDL
标志)-因此可以使用在非文件系统对象上。如果您仍然拥有完整的PIDL,这是获取图标索引的最简单方法,否则类似的方法应该可以工作:
int GetIShellItemSysIconIndex(IShellItem* psi)
{
PIDLIST_ABSOLUTE pidl;
int iIndex = -1;
if (SUCCEEDED(SHGetIDListFromObject(psi, &pidl)))
{
SHFILEINFO sfi{};
if (SHGetFileInfo(reinterpret_cast<LPCTSTR>(pidl), 0, &sfi, sizeof(sfi), SHGFI_PIDL | SHGFI_SYSICONINDEX))
iIndex = sfi.iIcon;
CoTaskMemFree(pidl);
}
return iIndex;
}
或使用Raymond Chen的建议:
int GetIconIndex(IShellItem item)
{
Int32 imageIndex;
PIDLIST_ABSOLUTE parent;
IShellFolder folder;
PITEMID_CHILD child;
//Use IParentAndItem to have the ShellItem
//cough up the IShellObject and child pidl it is wrapping.
(item as IParentAndItem).GetParentAndItem(out parent, out folder, out child);
try
{
//Now use IShellIcon to get the icon index from the folder and child
(folder as IShellIcon).GetIconOf(child, GIL_FORSHELL, out imageIndex);
}
finally
{
CoTaskMemFree(parent);
CoTaskMemFree(child);
}
return imageIndex;
}
原来
IShellFolder
有时不支持IShellIcon
。例如,尝试浏览一个zip文件。发生这种情况时,QueryInterface
的IShellFolder
的IShellIcon
失败。shellFolder.QueryInterface(IID_IShellIcon, out shellIcon); //<--fails with E_NOINTERFACE
但是
SHGetFileInfo
知道如何处理它。因此,最好不要尝试自己获取
IShellIcon
接口(interface)。将重担留给SHGetFileInfo
(至少直到Microsoft的某人记录了如何使用IShellIcon
为止)。