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

时间:2016-02-19 04:46:05

标签: windows shell winapi com

鉴于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函数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命名空间中使用与文件对应的项时,该方法有效。但是shell支持除文件系统中的文件和文件夹之外的东西。

来自IShellFolder的图标索引

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

  • IShellFolder thing 所在的文件夹,以及
  • PIDL :该文件夹中 thing 的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 :绑定到处理程序ID值(BHID)指定的项目的处理程序。
  • 比较:比较两个IShellItem对象。
  • GetAttributes :获取IShellItem对象的一组请求的属性。
  • GetDisplayName :获取IShellItem对象的显示名称。
  • GetParent :获取IShellItem对象的父级。

我希望通过Windows Property System可访问的IShellItem2具有与shell imagelist图标索引关联的属性。不幸的是,我没有看到任何:

提取图标的方法

在Windows Shell命名空间中获取与 thing 相关的图片are many standard ways

他们都没有:

  • 采取IShellItem
  • 返回索引

2 个答案:

答案 0 :(得分:4)

基本上似乎没有任何简单的方法可以解决这个问题。它根本没有在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的{​​{1}} IShellFolder会失败。

IShellIcon

然而shellFolder.QueryInterface(IID_IShellIcon, out shellIcon); //<--fails with E_NOINTERFACE 知道如何处理它。

最好不要尝试自己获取SHGetFileInfo界面。将繁重的工作留给IShellIcon(至少在Microsoft的某个人记录如何使用SHGetFileInfo之前)。

答案 1 :(得分:1)

在您的精彩调查中,您忘记了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;