Как получить индекс значка списка системных изображений 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 и новее)мы могли бы попросить оболочку дать нам индекс системного imagelist для значка элемента. Мы сделали это с помощью SHGetFileInfo. Функция SHGetFileInfo получает значок, задавая пространство имен оболочки для индекса значка в системном 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;
    }
}

это работает, когда вы используете элемент в пространстве имен оболочки, что соответствует файл. Но оболочка поддерживает все кроме файлов и папок в файловой системе.

значок индекса из IShellFolder

общее решение для получения информации об объекте в пространстве имен оболочки происходит от использования способа представления элемент в пространстве имен оболочки:

  • IShellFolder: папки, что вещь сидит вместе с
  • ребенок PIDL: идентификатор вещь в папке

что есть способы получить индекс списка изображений системы:

Int32 GetIconIndex(IShellFolder folder, PITEMID_CHILD childPidl)
{
   //Note: I actually have no idea how to do this.
}

но IShellFolder отсутствует; мы используем IShellItem теперь

начиная с Windows Vista,IShellItem стало предпочтительный API для навигации по оболочке. Windows 95 era API, чтобы сохранить IShellFolder+pidl пара был громоздким и подвержен ошибкам.

возникает вопрос: как с ним что-то делать? В частности, как получить индекс изображения в списке системных изображений элемента? Глядя на его методы, нет даже способа получить его Абсолют pidl:

  • BindToHandler: привязывается к обработчику для элемента, как указано значением идентификатора обработчика (BHID).
  • сравнить: сравнивает два объекта IShellItem.
  • методы getattributes: возвращает запрошенный набор атрибутов объекта IShellItem.
  • GetDisplayName: возвращает отображаемое имя объекта IShellItem.
  • GetParent: получает родитель объекта IShellItem.

я надеялся, что Система Свойств Windows, можно через IShellItem2, будет иметь свойство, связанное с индексом значка оболочки imagelist. К сожалению, я не вижу любой:

  • .DescriptionID
  • .InfoTipText
  • .InternalName
  • .Ссылка на сайт.TargetSFGAOFlagsStrings
  • .Ссылка на сайт.TargetUrl
  • .NamespaceCLSID

2 ответов


в основном кажется, что для этого нет простого метода. Он просто не был предоставлен в API.

в вашем вопросе вы говорите "но оболочка поддерживает все кроме файлов и папок в файловой системе.", что заставляет меня думать, что вы упустили это SHGetFileInfo фактически поддерживает использование PIDLs напрямую (с помощью SHGFI_PIDL флаг) - так это can используется для объектов, отличных от файловой системы. Если у вас все еще есть полный 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-файла. Когда это происходит,QueryInterface of IShellFolder на IShellIcon не удается.

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

пока SHGetFileInfo знает, как с этим справиться.

так лучше не пытайтесь получить IShellIcon интерфейс себя. Оставьте тяжелую работу SHGetFileInfo (по крайней мере, пока кто-то из документов Microsoft, как использовать 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;