如何获取系统镜像列表图标索引的IShellItem? [英] How to get system image list icon index of an IShellItem?
问题描述
如果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函数通过向系统imagelist中的图标索引询问shell命名空间来获取图标 a>:
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支持除了文件系统中的文件和文件夹之外的其他内容。
来自IShellFolder的图标索引
在shell命名空间中获取对象信息的一般解决方案来自于使用在shell命名空间中表示项的方式:
-
IShellFolder
: / li>
- 子
PIDL
:该文件夹中 li>
- 子
Int32 GetIconIndex(IShellFolder文件夹,PITEMID_CHILD childPidl)
{
//注意:我实际上不知道如何做到这一点。
}
但是IShellFolder现在正在使用IShellItem
从Windows Vista开始, IShellItem
https://msdn.microsoft.com/en-us/library/windows/desktop/bb761144(v=vs.85).aspx\">首选AP 我用于导航shell。 Windows 95时代的API必须保持 IShellFolder + pidl
blogs.msdn.microsoft.com/oldnewthing/20110831-00/?p=9763/\">麻烦,容易出错。
问题变成了:如何做它的东西?特别是,我如何获得图像索引在系统图像列表中的项目?看看它的方法,甚至没有办法得到它的绝对pidl:
- BindToHandler :绑定
- 比较:比较两个IShellItem对象。
- GetAttributes :获取IShellItem对象的一组请求属性。
- GetDisplayName :获取IShellItem的显示名称对象。
- GetParent :获取IShellItem对象的父对象。
我希望 Windows属性系统 ,可通过 IShellItem2
,将具有与shell图像列表图标索引相关联的属性。很抱歉,我没有看到任何内容:
- System.DescriptionID
- System.InfoTipText
- System.InternalName
- System.Link.TargetSFGAOFlagsStrings
- System.Link.TargetUrl
- System.NamespaceCLSID
- System.Shell.SFGAOFlagsStrings
提取图标的方法 b
$ b
是许多标准方法获得与 em>在Windows Shell命名空间中:
-
IExtractIcon
。返回HICON
。需要IShellFolder
+ pidl。如果失败,您可以使用SHDefExtractIcon -
SHDefExtractIcon
。返回HICON
。需要图标文件的完整路径 -
IThumbnailCache
。需要IShellItem
。返回缩图而不是图标 -
IShellImageFactory
。获取代表IShellItem
的$ bit -
IThumbnailProvider
。 Windows Vista替换IExtractImage
-
IExtractImage
。需要IShellFolder
+pidl
。 -
SHGetFileInfo
。需要完整的文件路径或绝对pidl
不包括:
- 取一个IShellItem
- 返回索引
基本上,似乎没有任何简单的方法。
在您的问题中,您说但是shell支持除文件系统中的文件和文件夹之外的事情。 / em>,这让我想你已经忽略了 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文件夹;
PITEMID_CHILD child;
//使用IParentAndItem使ShellItem
//咳嗽IShellObject和它包装的子pidl。
(item为IParentAndItem).GetParentAndItem(out parent,out folder,out child);
try
{
//现在使用IShellIcon从文件夹获取图标索引,子
(文件夹为IShellIcon).GetIconOf(child,GIL_FORSHELL,out imageIndex);
}
finally
{
CoTaskMemFree(parent);
CoTaskMemFree(child);
}
return imageIndex;
}
结果证明, IShellFolder
有时不支持 IShellIcon
。例如,尝试在zip文件中浏览。当发生这种情况时, IShellFolder
的
QueryInterface
。
shellFolder.QueryInterface(IID_IShellIcon,out shellIcon); //< - 失败与E_NOINTERFACE
但 SHGetFileInfo
知道如何处理它。
所以最好不要尝试自己得到一个 IShellIcon
接口。离开繁重的提升到 SHGetFileInfo
(至少直到有人从Microsoft文档如何使用 IShellIcon
)。
Given a Windows Vista or newer IShellItem
, how do i get the system image list icon index associated with that item?
For example (pseudo-code):
IShellItem networkFolder = SHGetKnownFolderItem(FOLDERID_NetworkFolder, 0, 0, IShellItem);
Int32 iconIndex = GetSmallSysIconIndex(networkFolder);
Int32 GetSmallSysIconIndex(IShellItem item)
{
//TODO: Ask Stackoverflow
}
Background
In the olden days (Windows 95 and newer), we could ask the shell to give us the system imagelist index for an item's icon. We did it using SHGetFileInfo
. The SHGetFileInfo function 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;
}
}
That works when you are using an item in the shell namespace that corresponds to a file. But the shell supports things besides files and folders in the filesystem.
Icon Index from IShellFolder
The general solution for getting information about an object in the shell namespace comes from using the way you represent an item in the shell namespace:
IShellFolder
: the folder that the thing sits in, along with- child
PIDL
: the id of the thing in that folder
From that there are 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.
}
But IShellFolder is out; we're using IShellItem now
Starting with Windows Vista, IShellItem
became the preferred API for navigating the shell. The Windows 95 era API of having to keep an IShellFolder
+pidl
pair around was cumbersome, and error prone.
The question becomes: How to do stuff with it? In particular, how do i get the image index in the system image list of the item? Looking at its methods, there isn't even a way to get its absolute pidl:
- BindToHandler: Binds to a handler for an item as specified by the handler ID value (BHID).
- Compare: Compares two IShellItem objects.
- GetAttributes: Gets a requested set of attributes of the IShellItem object.
- GetDisplayName: Gets the display name of the IShellItem object.
- GetParent: Gets the parent of an IShellItem object.
I was hoping that the Windows Property System, accessible through IShellItem2
, would have a property associated with the shell imagelist icon index. Unfortunately, i don't see any:
- System.DescriptionID
- System.InfoTipText
- System.InternalName
- System.Link.TargetSFGAOFlagsStrings
- System.Link.TargetUrl
- System.NamespaceCLSID
- System.Shell.SFGAOFlagsStrings
Ways of extracting icons
There are many standard ways of getting the picture that goes with a thing in the Windows Shell Namespace:
IExtractIcon
. Returns anHICON
. RequiresIShellFolder
+pidl. If it fails you can use SHDefExtractIconSHDefExtractIcon
. Returns anHICON
. Requires full path to icon fileIThumbnailCache
. RequiresIShellItem
. Returns thumbnail, not iconIShellImageFactory
. Gets a bitmap that represents anIShellItem
IThumbnailProvider
. Windows Vista replacement forIExtractImage
IExtractImage
. RequiresIShellFolder
+pidl
.SHGetFileInfo
. Requires full file path, or an absolute pidl
None of them:
- take an IShellItem
- return an index
Basically it doesn't seem that there's any easy method for this. It simply hasn't been provided in the API.
In your question you say "But the shell supports things besides files and folders in the filesystem.", which makes me think you have overlooked that SHGetFileInfo
does actually support using PIDLs directly (with the SHGFI_PIDL
flag) - so it can be used on non-filesystem objects. If you still have the full PIDL this is the easiest way to get an icon index, otherwise something like this should hopefully work:
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;
}
Or using Raymond Chen's suggestion:
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;
}
Turns out that IShellFolder
doesn't support the IShellIcon
sometimes. For example attempting to browse inside a zip file. When that happens, the QueryInterface
of IShellFolder
for IShellIcon
fails.
shellFolder.QueryInterface(IID_IShellIcon, out shellIcon); //<--fails with E_NOINTERFACE
Yet SHGetFileInfo
knows how to handle it.
So best to not try to get an IShellIcon
interface yourself. Leave the heavy lifting to SHGetFileInfo
(at least until someone from Microsoft documents how to use IShellIcon
).
这篇关于如何获取系统镜像列表图标索引的IShellItem?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!