给定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:
  • BindToHandler :绑定(bind)到由处理程序ID值(BHID)指定的项目的处理程序。
  • 比较:比较两个IShellItem对象。
  • GetAttributes :获取IShellItem对象的一组请求的属性。
  • GetDisplayName :获取IShellItem对象的显示名称。
  • GetParent :获取IShellItem对象的父级。

  • 我希望可以通过Windows Property System访问的 IShellItem2 具有与 shell imagelist图标索引相关联的属性。不幸的是,我没有看到任何东西:
  • System.DescriptionID
  • System.InfoTipText
  • System.InternalName
  • System.Link.TargetSFGAOFlagsStrings
  • System.Link.TargetUrl
  • System.NamespaceCLSID
  • System.Shell.SFGAOFlagsStrings

  • 提取图标的方式

    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

  • 没有一个:
  • 接受IShellItem
  • 返回索引
  • 最佳答案

    基本上,似乎没有任何简单的方法可以做到这一点。根本没有在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文件。发生这种情况时,QueryInterfaceIShellFolderIShellIcon失败。
    shellFolder.QueryInterface(IID_IShellIcon, out shellIcon); //<--fails with E_NOINTERFACE
    

    但是SHGetFileInfo知道如何处理它。

    因此,最好不要尝试自己获取IShellIcon接口(interface)。将重担留给SHGetFileInfo(至少直到Microsoft的某人记录了如何使用IShellIcon为止)。

    10-05 23:44