Extract all icons from an EXE without using ExtractIconEx

I need to extract all icons from an EXE and save them as disk files, but I cannot use the simplest solution (ExtractIconEx) because I need to extract the ICO files in a way that preserves the large-size icons, even when the code runs on systems running Windows XP. I will later extract the wanted icons (128x128 and other vista size icons) using other code, but I need a way to extract all icons, with all their resources, as they are present in the EXE, regardless of whether the PC my code runs on is running XP, Vista, or Win7.

So far I know that icons are in the RT_GROUP_ICONS.

I know some people must have done this before in Delphi, because utilities like IcoFX and resource-explorer seem to have done this. I once remember seeing a completely open-source Delphi tool that would do it, but it was years ago.

To restate the problem I have with ExtractIconEx - It will not access the entire .ico file, it will only extract the supported icon resource formats, which ever single resolution (size) that the platform supports. So on XP, for example, it will extract a 32x32 or 48x48 icon, but not a Vista-format 128x128 icon.

Update: This is a modified version of the accepted answer that solves my future-proofing worry. if somehow the function we are calling was to disappear from User32.dll in a future windows version, I would want it to fail more gracefully, than to fail to load up my whole application.

unit ExtractIconUtils;

interface

uses Graphics,Forms,Windows;

//----------------------------------------------------------------------------
// ExtractIcons
// Call "private" MS Api to extract Icon file. This calls a publically
// documented function marked as deprecated in the MSDN documentation.
// It was no doubt Not Originally Intended to be documented, or publically
// accessed, but it provides functionality that its hard to live without.
// It exists on Windows 2000, XP, Vista, and Windows7, but might not exist
// in some future Windows version (released after year 2011).
//
// uses global   hUserDll    : THandle;
//----------------------------------------------------------------------------
function ExtractIcons(exeFilename,icoOutFileName:String;icoSize:Integer):Boolean;



var
  hUserDll    : THandle;





implementation



function ExtractIcons(exeFilename,icoOutFileName:String;icoSize:Integer):Boolean;
const
{$ifdef UNICODE}
 ExtractProcName='PrivateExtractIconsW';
{$else}
 ExtractProcName='PrivateExtractIconsA';
{$endif}
type
  TExtractFunc = function(lpszFile: PChar; nIconIndex, cxIcon, cyIcon: integer; phicon: PHANDLE; piconid: PDWORD; nicon, flags: DWORD): DWORD; stdcall;
var
  hIcon   : THandle;
  nIconId : DWORD;
  Icon    : TIcon;
  PrivateExtractIcons:TExtractFunc;
begin
  result := false;
  if (hUserDll<4) then begin
    hUserDll := LoadLibrary('user32.dll');
    if (hUserDll<4) then exit;
  end;

     { PrivateExtractIcons:
        MSDN documentation says that this function could go away in a future windows
        version, so we must try to load it, and if it fails, return false, rather than
        doing a static DLL import.
     }
    PrivateExtractIcons :=     GetProcAddress(hUserDll, ExtractProcName);

    if not Assigned(PrivateExtractIcons) then exit;

    //extract a icoSize x icoSize  icon where icoSize is one of 256,128,64,48,32,16
    if PrivateExtractIcons ( PWideChar(exeFilename),
                            0, icoSize, icoSize, @hIcon, @nIconId, 1, LR_LOADFROMFILE) <>0 then
    try
      Icon:=TIcon.Create;
      try
        Icon.Handle:=hIcon;
        Icon.SaveToFile(icoOutFileName);
        result := true;
      finally
        Icon.Free;
      end;
    finally
      DestroyIcon (hIcon);
    end;
end ;


initialization
  // none

finalization
   if (hUserDll>4) then
      FreeLibrary(hUserDll);

end.

6
задан Community 23 May 2017 в 12:31
поделиться