Страница 1 из 2

Слои оверлея

Добавлено: Сб ноя 28, 2009 12:07 am
Max Diesel
Разыскивается код на Delphi/C++Builder, позволяющий получить у системы оверлейные слои значков. Буду очень признателен за конкретную помощь по данному вопросу.

Re: Слои оверлея

Добавлено: Пт дек 04, 2009 8:34 pm
admini
Только что рылся в MSDN там что-то есть такое и такое.

Добавлено: Вс дек 06, 2009 6:36 pm
Max Diesel
При моих умениях взаимодействия со специфическими MS-функциями ничего не получается...

Re: Слои оверлея

Добавлено: Пн дек 07, 2009 9:53 am
admini
Объясните пожалуйста что конкретно нужно, а то я не понял смысла чего надо сделать. С оверлеями не работал и только понял из описания, что это маленький значок типа стрелочки, рисуемый поверх ярлыков, к примеру.

Добавлено: Пн дек 07, 2009 7:04 pm
Max Diesel
Насколько мне известно, оверлейный слой работает именно по такому принципу - накладывается поверх какой-либо иконки. Если запросить у ОС индекс иконки файла, то иконка соответствующая индексу (расположенная в системном ImageList'е) не будет содержать оверлейных слоев. Если же запросить не индекс, а саму иконку (получить ее как объект), то ОС выдаст ее с уже наложенными оверлейными слоями (сейчас именно такой принцип и используется в программе). Меня интересует способ получения непосредственно чего-то вроде "индекса слоя". Было бы замечательно если бы некий код примера содержал следующие фазы:
1. получение индекса иконки файла (впрочем эта фаза-то у меня есть),
2. получение индекса оверлейного слоя для этого файла,
3. прорисовка результирующей иконки на canvas какого-либо объекта через функцию DrawIconEx или какую-либо другую (в данном случае можно было бы наложить оверлейный слой на любую другую иконку, индекс которой известен).

Re: Слои оверлея

Добавлено: Пн дек 07, 2009 11:50 pm
admini

Код: Выделить всё

код удалил. смотрите ниже

Добавлено: Вт дек 08, 2009 1:23 pm
Max Diesel
Первый вариант (при SHGFI_ADDOVERLAYS) как раз и использован в программе на данный момент - получение иконки с объединенными слоями. А вот насчет второго варианта проблема... переменная overlayIndex вместо номера иконки слоя получает значение -1. Возможно я при компиляции делаю что-то не так, но именно такой исход и получался у меня всегда ранее... Удается ли при тестировании получить другие результаты?

Re: Слои оверлея

Добавлено: Вт дек 08, 2009 3:24 pm
admini
хммм. мистика какая то. дома код работает а на работе тоже возвращает -1, а SHGetIconOverlayIndex всегда какое-то странное значение - 8. погляжу еще в чем дело и можно уточнить а зачем разделять иконки и оверлеи когда сразу отрисовывать?

Добавлено: Вт дек 08, 2009 3:36 pm
Max Diesel
Отделение иконки от оверлейного слоя нужно для случаев когда например установлены несистемные иконки каталогов, но при этом должны быть отображены оверлеи (чтобы не получалось месиво из иконок, ведь оверлейные слои всегда по умолчанию налагаются на системные иконки каталогов). Да и с точки зрения памяти явно рациональнее получать не готовую иконку как объект, а лишь однобайтовый индекс оверлейного слоя (да и с прорисовкой оверлейных иконок при входе в каталог получается не так как хотелось, ведь каждый раз производится перечитывание всех иконок без кэширования).

И кстати я обратил внимание на тот факт, что если пройтись по полученному ImageList'у напрямую (прорисовывать все иконки от нулевой по очереди), то необходимых оверлейных среди существующих нет (хотя некоторые другие оверлейные все-таки есть). Отсюда напрашивается вывод что может быть система выдает не системный ImageList, а какой-то его урезанный аналог?

Re: Слои оверлея

Добавлено: Вт дек 08, 2009 8:08 pm
admini

Код: Выделить всё

код удалил. смотрите ниже

Re: Слои оверлея

Добавлено: Ср дек 09, 2009 10:29 am
admini
Кстати информация - придурки из MS убрали оверлеи у расшаренных папок в Win7 и поэтому данная функа не работает для расшаренных папок (вернет -1) но анрил как то отрисовывает с объединенными оверлеями...

Re: Слои оверлея

Добавлено: Ср дек 09, 2009 1:20 pm
admini

Код: Выделить всё

код удалил. смотрите ниже

Re: Слои оверлея

Добавлено: Ср дек 09, 2009 1:41 pm
admini
В общем теперь всё работает. Если уже пробовали предыдущий код, то удалите его и пробуйте этот :)
Требования: Windows 2000 или выше и не забывать юзать CoInitializeEx где необходимо.

Код: Выделить всё

uses
  ActiveX, ShlObj, ShLwApi, CommCtrl;

const
  IID_IShellIconOverlay: TGUID = (
    D1:$7D688A70; D2:$C613; D3:$11D0; D4:($99, $9B, $00, $C0, $4F, $D6, $55, $E1));

  IID_IShellIconOverlayManager: TGUID = (
    D1:$F10B5E34; D2:$DD3B; D3:$42A7; D4:($AA, $7D, $2F, $4E, $C5, $4B, $B0, $9B));

  CLSID_CFSIconOverlayManager: TGUID = (
    D1:$63B51F81; D2:$C868; D3:$11D0; D4:($99, $9C, $00, $C0, $4F, $D6, $55, $E1));

type
  IShellIconOverlayManager = interface
    ['{F10B5E34-DD3B-42A7-AA7D-2F4EC54BB09B}']
    function GetFileOverlayInfo(pwszPath: LPCWSTR; dwAttrib: DWORD;
      var iIndex: Integer; dwFlags: DWORD): HRESULT; stdcall;
    function GetReservedOverlayInfo(pwszPath: LPCWSTR; dwAttrib: DWORD;
      var iIndex: Integer; dwFlags: DWORD; iReservedID: Integer): HRESULT; stdcall;
    function RefreshOverlayImages(dwFlags: DWORD): HRESULT; stdcall;
    function LoadNonloadedOverlayIdentifiers(): HRESULT; stdcall;
    function OverlayIndexFromImageIndex(iImage: Integer;
      var iIndex: Integer; fAdd: BOOL): HRESULT; stdcall;
  end;

const
  SIOM_OVERLAYINDEX      = 1;
  SIOM_ICONINDEX         = 2;
  SIOM_RESERVED_SHARED   = 0;
  SIOM_RESERVED_LINK     = 1;
  SIOM_RESERVED_SLOWFILE = 2;

const
  SHGFI_ICON              = $000000100;
  SHGFI_DISPLAYNAME       = $000000200;
  SHGFI_TYPENAME          = $000000400;
  SHGFI_ATTRIBUTES        = $000000800;
  SHGFI_ICONLOCATION      = $000001000;
  SHGFI_EXETYPE           = $000002000;
  SHGFI_SYSICONINDEX      = $000004000;
  SHGFI_LINKOVERLAY       = $000008000;
  SHGFI_SELECTED          = $000010000;
  SHGFI_LARGEICON         = $000000000;
  SHGFI_SMALLICON         = $000000001;
  SHGFI_OPENICON          = $000000002;
  SHGFI_SHELLICONSIZE     = $000000004;
  SHGFI_PIDL              = $000000008;
  SHGFI_USEFILEATTRIBUTES = $000000010;    
  SHGFI_ADDOVERLAYS       = $000000020;
  SHGFI_OVERLAYINDEX      = $000000040;

type
  _SHFILEINFOW = packed record
    hIcon: HICON;
    iIcon: Integer;
    dwAttributes: DWORD;
    szDisplayName: array [0..MAX_PATH-1] of  WideChar;
    szTypeName: array [0..79] of WideChar;            
  end;

function SHGetFileInfoW(pszPath: LPCWSTR; dwFileAttributes: DWORD;
  var psfi: _SHFILEINFOW; cbFileInfo, uFlags: UINT): DWORD; stdcall; external 'SHELL32.DLL';

function QueryShlObjIconOverlayIndex(filePath: PWideChar; var iIndex: Integer): Boolean;
var
  pathBuf: PWideChar;
  desktopFolder, folder: IShellFolder;
  folderID, fileID: PItemIDList;
  shellIconOverlay: IShellIconOverlay;
  overlayIndex, overlayIconIndex: Integer;
begin
  Result := False;
  iIndex := -1;
  if not PathFileExistsW(filePath) then Exit;
  GetMem(pathBuf, (lstrlenW(filePath) + 1) shl 1);
  try
    lstrcpyW(pathBuf, filePath);
    StrTrimW(pathBuf, '\');
    PathRemoveFileSpecW(pathBuf);
    SHGetDesktopFolder(desktopFolder);
    if not Assigned(desktopFolder) then Exit;     
    try
      desktopFolder.ParseDisplayName(0, nil,
        pathBuf, PULONG(nil)^, folderID, PULONG(nil)^);
      if not Assigned(folderID) then Exit;
      try
        desktopFolder.BindToObject(folderID, nil, IID_IShellFolder, folder);
        if not Assigned(folder) then Exit;
        try
          lstrcpyW(pathBuf, PathFindFileNameW(filePath));
          StrTrimW(pathBuf, '\');
          folder.ParseDisplayName(0, nil,
            pathBuf, PULONG(nil)^, fileID, PULONG(nil)^);
          if not Assigned(fileID) then Exit;
          try
            folder.QueryInterface(IID_IShellIconOverlay, shellIconOverlay);
            if not Assigned(shellIconOverlay) then Exit;
            try
              shellIconOverlay.GetOverlayIndex(fileID, overlayIndex);
              shellIconOverlay.GetOverlayIconIndex(fileID, overlayIconIndex);
              if (overlayIndex >= overlayIconIndex) then
                iIndex := overlayIndex - 1
              else
                iIndex := overlayIconIndex; 
            finally
              shellIconOverlay := nil;
            end;
          finally
            CoTaskMemFree(fileID);
          end;
        finally
          folder := nil;
        end;
      finally
        CoTaskMemFree(folderID);
      end;
    finally
      desktopFolder := nil;
    end;
  finally
    FreeMem(pathBuf);
  end;
  Result := True;
end;

function QueryReservedIconOverlayIndex(iReservedID: Integer; var iIndex: Integer): Boolean;
var
  shellIconOverlayManager: IShellIconOverlayManager;
begin
  Result := False;
  CoCreateInstance(CLSID_CFSIconOverlayManager, nil,
    CLSCTX_INPROC_SERVER, IID_IShellIconOverlayManager, shellIconOverlayManager);
  if not Assigned(shellIconOverlayManager) then Exit;
  try
    shellIconOverlayManager.GetReservedOverlayInfo(nil, 0, iIndex, SIOM_ICONINDEX, iReservedID);
  finally
    shellIconOverlayManager := nil;
  end;
  Result := True;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  filePath: WideString;
  shfi: _SHFILEINFOW;
  hImgList: integer;
  overlayIndex: Integer;
begin
  filePath := StringToOleStr(Edit1.Text);

  FillChar(shfi, SizeOf(shfi), 0);
  hImgList := SHGetFileInfoW(PWideChar(filePath), 0,
    shfi, SizeOf(shfi), SHGFI_SMALLICON or SHGFI_SYSICONINDEX or SHGFI_ATTRIBUTES);

  QueryShlObjIconOverlayIndex(PWideChar(filePath), overlayIndex);

  if (overlayIndex < 0) and (shfi.dwAttributes and SFGAO_SHARE <> 0) then
    QueryReservedIconOverlayIndex(SIOM_RESERVED_SHARED, overlayIndex);

  Refresh();
  ImageList_Draw(hImgList, shfi.iIcon, Canvas.Handle, 200, 100, ILD_TRANSPARENT);
  ImageList_Draw(hImgList, overlayIndex, Canvas.Handle, 200, 100, ILD_TRANSPARENT);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  CoInitializeEx(nil, COINIT_APARTMENTTHREADED);
  FileInitIcons(True);
end;

Добавлено: Ср дек 09, 2009 9:23 pm
Max Diesel
Большое спасибо, код действительно работает так как надо. Весьма впечатляюще если учесть тот факт что прошла всего пара дней (я за гораздо больший период времени так и не смог разобраться в причине неотображения/неполучения оверлейных слоев).

Re: Слои оверлея

Добавлено: Чт дек 10, 2009 12:29 am
admini
Я решил еще проанализировать ситуацию в плане документированности функций и рекомендаций из MSDN и оформить все в одном примере. Работает также начиная с вин2000. Думаю это наиболее подходящий вариант.