Windows 如何获取IShellItem的系统映像列表图标索引?

Windows 如何获取IShellItem的系统映像列表图标索引?,windows,shell,winapi,com,Windows,Shell,Winapi,Com,给定Windows Vista或更新版本的IShellItem,如何获取与该项目关联的系统映像列表图标索引 例如(伪代码): 背景 在过去(Windows95和更新版本),我们可以要求shell为我们提供项目图标的系统imagelist索引。我们是用电脑做的。SHGet-File-Info功能: 当您在shell名称空间中使用与文件对应的项时,该选项将起作用。但是shell除了支持文件系统中的文件和文件夹外,还支持其他功能 IShellFolder中的图标索引 获取shell名称空间中对象信息的

给定Windows Vista或更新版本的IShellItem,如何获取与该项目关联的系统映像列表图标索引

例如(伪代码):

背景 在过去(Windows95和更新版本),我们可以要求shell为我们提供项目图标的系统imagelist索引。我们是用电脑做的。SHGet-File-Info功能:

当您在shell名称空间中使用与文件对应的项时,该选项将起作用。但是shell除了支持文件系统中的文件和文件夹外,还支持其他功能

IShellFolder中的图标索引

获取shell名称空间中对象信息的一般解决方案来自使用在shell名称空间中表示项的方式:

  • IShellFolder
    :物品所在的文件夹,以及
  • child
    PIDL
    :该文件夹中对象的id
从中可以看出:

但IShell文件夹已出;我们现在正在使用IShellItem

从Windows Vista开始,
IShellItem
就成了导航shell的工具。Windows95时代的API必须保留一个
IShellFolder
+
pidl

问题变成了:如何使用它?特别是,如何在项目的系统映像列表中获取映像索引?从它的方法来看,甚至没有一种方法可以获得它的绝对pidl:

  • BindToHandler:绑定到由处理程序ID值(BHID)指定的项目的处理程序
  • 比较:比较两个IShellItem对象
  • GetAttributes:获取IShellItem对象的请求属性集
  • GetDisplayName:获取IShellItem对象的显示名称
  • GetParent:获取IShellItem对象的父对象
我希望可以通过访问的将具有与shell imagelist图标索引关联的属性。不幸的是,我没有看到任何:

提取图标的方法

在Windows Shell名称空间中,有一种获取图片的方法:

  • 。返回一个
    HICON
    。需要
    IShellFolder
    +pidl。如果失败了
  • 。返回一个
    HICON
    。需要图标文件的完整路径
  • 。需要
    IShellItem
    。返回缩略图,而不是图标
  • 。获取表示IShellItem的位图
  • 。Windows Vista替换为
    IExtractImage
  • 。需要
    IShellFolder
    +
    pidl
  • 。需要完整文件路径或绝对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;
}

结果是,
IShellFolder
有时不支持
IShellIcon
。例如,尝试在zip文件中浏览。发生这种情况时,
IShellIcon
IShellFolder
QueryInterface
将失败

shellFolder.QueryInterface(IID_IShellIcon, out shellIcon); //<--fails with E_NOINTERFACE

shellFolder.QueryInterface(IID_IShellIcon,out shellIcon)// 在您伟大的调查中,您忘记了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;

使用IParentAndItem将IShellItem分解为IShellFolder和pidl。然后使用IShellIcon::GetIconOf.@RaymondChen当ShellFolder是zip外壳扩展名时,调用
IShellFolder上的
QueryInterface(IID\u ShellIcon)
会失败,因为
E\u NOINTERFACE
。同时,使用
SHGetFileInfo
SHGetIDListFromObject
返回的绝对pidl确实能够返回图标索引。直接进入IShellIcon
糟糕的形式,最好依靠
SHGetFileInfo
来完成所有这些边缘案例的繁重工作?SHGetFileInfo使用IExtractCon,这比IShellIcon效率低(但是,如果IShellIcon不可用,你别无选择)。我将把它交给Jonathan,因为他有我需要的那部分:
SHGetIDListFromObject
。但是,如果我有了
IShellFolder
和child
PIDL
,那么直接转到
IShellIcon
似乎是更好的方法。您还提供了
IShellIcon
的正确且有效的示例用法。
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;
}
shellFolder.QueryInterface(IID_IShellIcon, out shellIcon); //<--fails with E_NOINTERFACE
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;