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.
PrivateExtractIcons
function ? msdn.microsoft.com/en-us/library/ms648075 – RRUZDestroyIcon
is not needed sinceIcon.Free
will handle that via internalFImage: TIconImage
. – kobik