鉴于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给我们一个项目的图标的系统图像列表索引。 我们使用SHGetFileInfo
。 SHGetFileInfo函数通过询问shell命名空间获取系统图像列表中的图标索引来获取图标 :
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命名空间中使用与文件相对应的项目时,这将起作用。 但是shell支持除了文件系统中的文件和文件夹之外的东西。
来自IShellFolder的图标索引
获取shell命名空间中的对象信息的一般解决scheme来自于使用您在shell命名空间中表示项目的方式:
IShellFolder
: 东西所在的文件夹,以及 PIDL
:该文件夹中的东西的ID 从这里可以得到系统映像列表索引 :
Int32 GetIconIndex(IShellFolder folder, PITEMID_CHILD childPidl) { //Note: I actually have no idea how to do this. }
但是IShellFolder出来了; 我们现在正在使用IShellItem
从Windows Vista开始, IShellItem
成为用于导航shell 的首选AP我。 不得不保持一个IShellFolder
+ pidl
对的Windows 95时代的API 是麻烦的,并且容易出错 。
问题就变成了:如何用它做东西? 特别是,我如何获得项目的系统图像列表中的图像索引? 看看它的方法,甚至没有办法得到它的绝对pidl:
我希望可以通过IShellItem2
访问的Windows属性系统将有一个与shell imagelist图标索引关联的属性。 不幸的是,我没有看到任何:
提取图标的方法
在Windows Shell命名空间中有许多标准的获取图片的方法 :
IExtractIcon
。 返回一个HICON
。 需要IShellFolder
+ pidl。 如果失败,可以使用SHDefExtractIcon SHDefExtractIcon
。 返回一个HICON
。 需要图标文件的完整path IThumbnailCache
。 需要IShellItem
。 返回缩略图,而不是图标 IShellImageFactory
。 获取一个表示一个IShellItem
的位图 IThumbnailProvider
。 Windows Vistareplace为IExtractImage
IExtractImage
。 需要IShellFolder
+ pidl
。 SHGetFileInfo
。 需要完整的文件path,或绝对pidl 他们都不是:
基本上,这似乎没有任何简单的方法。 它只是没有在API中提供。
在你的问题中,你会说“但是shell支持除文件系统中的文件和文件夹之外的东西。” ,这让我觉得你忽略了SHGetFileInfo
实际上直接支持使用SHGFI_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; }
或者使用雷蒙德的建议:
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文件。 发生这种情况时, IShellIcon
的IShellFolder
的QueryInterface
失败。
shellFolder.QueryInterface(IID_IShellIcon, out shellIcon); //<--fails with E_NOINTERFACE
然而SHGetFileInfo
知道如何处理它。
所以最好不要尝试自己获得一个IShellIcon
接口。 将重任留给SHGetFileInfo
(至少直到微软的某个人文档如何使用IShellIcon
)。
在你的伟大的调查中,你忘记了IShellIcon接口。 它甚至可以在Windows XP中使用。
function GetIconIndex(AFolder: IShellFolder; AChild: PItemIDList): Integer; overload; var ShellIcon: IShellIcon; R: HRESULT; begin OleCheck(AFolder.QueryInterface(IShellIcon, ShellIcon)); try R := ShellIcon.GetIconOf(AChild, 0, Result); case R of S_OK:; S_FALSE: Result := -1; // icon can not be obtained for this object else OleCheck(R); end; finally ShellIcon := nil; end; end;