0

I want to update my applications to be fully scalable, so every image in the app must be resized when the user chooses another scale. The icons for the user interface buttons I keep them as multi-size icon in the app resources and load them with LoadIconWithScaleDown function when needed.

Now I want to design a shell drive toolbar and use system icons for drive buttons and I found that I can get the system icons with SHGetFileInfo:

uses ShellApi;

procedure TForm1.Button1Click(Sender: TObject);
var FileInfo: TSHFileInfo;
    Ico: TIcon;
begin
 Ico:= TIcon.Create;
 SHGetFileInfo(PChar('C:'), 0, FileInfo, SizeOf(FileInfo), SHGFI_ICON);
 Ico.Handle:= FileInfo.hIcon;
 TestBtn.LoadImgFromIcon(Ico);
 DestroyIcon(FileInfo.hIcon);
 Ico.Free;
end;

But it seems that this function returns only 16 and 32 pixel icons. I see no way to specify the size of the icon I want. And I find it hard to believe that Windows has only 16 and 32 pixel icons. Those displayed in MyComputer ar bigger that 32 pixels...

enter image description here

So, do you know a way to get a custom sized system icon ? Something equivalent with the LoadIconWithScaleDown that returns good quality pictures ? I don't what to upscale them from 32 pixel icons...

Marus Gradinaru
  • 2,824
  • 1
  • 26
  • 55
  • This is an older answer on SO for C#, but may help here, too: [https://stackoverflow.com/a/28015423/13984925](https://stackoverflow.com/a/28015423/13984925) – Uwe Z Feb 04 '22 at 13:52

1 Answers1

0

I think I solved this one too... I never knew that my first love, LoadIconWithScaleDown, can load icons from dll's too. And the dll name can be found with SHGetFileInfo. The only thing that seems strange to me is, why SHGetFileInfo returns the icon idex as a negative value... ?

Update: After I read Remy's comment I improved the code so that it can extract icons from dll or exe, by resource ID (negative values) or icon index (positive values).

I wish I could have extracted the shell extension icons in this way as well, but it seems that SHGetFileInfo does not return icon location for file extensions, e.g SHGetFileInfo(PChar('D:\image1.jpg'),.... So, if anyone knows how we can extend this code to work for shell extension as well, I would really appreciate it.

uses ShellApi;

const
  LOAD_LIBRARY_AS_IMAGE_RESOURCE = $00000020;

type
  PResEnumData = ^TResEnumData;
  TResEnumData = record
   Idx, Count: Integer;
   ResName: LPTSTR;
  end;

function LoadIconWithScaleDown(hinst: HINST; pszName: LPCWSTR; cx: Integer;
 cy: Integer; var phico: HICON): HResult; stdcall; external 'ComCtl32';


function EnumResNameProc(hModule: HMODULE; lpszType: LPCTSTR; lpszName: LPTSTR;
 lParam: LONG_PTR): BOOL; stdcall;
var Data: PResEnumData;
begin
 Data:= PResEnumData(lParam);
 Inc(Data.Count);
 if Data.Count < Data.Idx then Result:= True
  else begin
   Data.ResName:= lpszName;
   Result:= False;
  end;
end;

function GetIconResNameByIndex(Module: HMODULE; Index: Integer): LPTSTR;
var Data: TResEnumData;
begin
 Data.Idx:= Index; Data.Count:= -1; Data.ResName:= nil;
 EnumResourceNames(Module, RT_GROUP_ICON, @EnumResNameProc, NativeInt(@Data));
 Result:= Data.ResName;
end;

procedure TForm1.Button1Click(Sender: TObject);
var FileInfo: TSHFileInfo;
    Ico: TIcon;
    hI: HICON;
    hMod: HMODULE;
    FileName, Ext: String;
    ResName: LPTSTR;
begin
 if SHGetFileInfo(PChar('c:'), 0, FileInfo, SizeOf(FileInfo), SHGFI_ICONLOCATION) > 0 then begin
  FileName:= FileInfo.szDisplayName;
  Ext:= LowerCase(ExtractFileExt(FileName));
  if (Ext = '.dll') or (Ext = '.exe') then begin
   Ico:= TIcon.Create; hI:= 0;
   hMod:= LoadLibraryEx(PChar(FileName), 0, LOAD_LIBRARY_AS_IMAGE_RESOURCE or LOAD_LIBRARY_AS_DATAFILE);
   if hMod > 0 then begin
    if FileInfo.iIcon < 0
     then ResName:= MAKEINTRESOURCE(Abs(FileInfo.iIcon))
     else ResName:= GetIconResNameByIndex(hMod, FileInfo.iIcon);
    if Assigned(ResName) and (LoadIconWithScaleDown(hMod, ResName, 64, 64, hI) = S_OK)
     and (hI > 0) then begin
      Ico.Handle:= hI;
      TestBtn.LoadImgFromIcon(Ico);
     end;
    FreeLibrary(hmod);
   end;
   Ico.Free;
  end;
 end
end;
Marus Gradinaru
  • 2,824
  • 1
  • 26
  • 55
  • "*why SHGetFileInfo returns the icon idex as a negative value... ?*" - see [How the shell converts an icon location into an icon](https://devblogs.microsoft.com/oldnewthing/20100505-00/?p=14153) – Remy Lebeau Feb 04 '22 at 15:54
  • I would have crawled the Registry manually, just like [How to get the icon path and index associated with a file type?](https://stackoverflow.com/q/39373357/4299358) describes, along with its shortcomings - it would give me `%SystemRoot%\System32\imageres.dll,-83` for PNG files and I'd be happy enough with that. – AmigoJack Feb 04 '22 at 22:25