【问题标题】:Extract all icons from an EXE without using ExtractIconEx在不使用 ExtractIconEx 的情况下从 EXE 中提取所有图标
【发布时间】:2011-08-22 18:06:17
【问题描述】:

我需要从 EXE 中提取所有图标并将它们保存为磁盘文件,但我不能使用最简单的解决方案 (ExtractIconEx),因为我需要以保留大尺寸图标的方式提取 ICO 文件,即使代码在运行 Windows XP 的系统上运行。稍后我将使用其他代码提取想要的图标(128x128 和其他 vista 大小的图标),但我需要一种方法来提取所有图标及其所有资源,因为它们存在于 EXE 中,无论我的代码是否在 PC 上运行on 正在运行 XP、Vista 或 Win7。

目前我知道图标在RT_GROUP_ICONS。

我知道有些人以前在 Delphi 中必须这样做过,因为 IcoFX 和 resource-explorer 等实用程序似乎已经这样做了。我曾经记得看到一个完全开源的 Delphi 工具可以做到这一点,但那是几年前的事了。

重申我在使用 ExtractIconEx 时遇到的问题 - 它不会访问整个 .ico 文件,它只会提取支持的图标资源格式,即平台支持的单一分辨率(大小)。因此,例如在 XP 上,它将提取 32x32 或 48x48 图标,但不会提取 Vista 格式的 128x128 图标。

更新:这是已接受答案的修改版本,解决了我对未来的担忧。如果我们调用的函数以某种方式在未来的 Windows 版本中从 User32.dll 中消失,我希望它更优雅地失败,而不是无法加载我的整个应用程序。

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.

【问题讨论】:

  • Vista 图标是 256px PNG 图片,不是 128px,FWIW
  • 你试过PrivateExtractIcons函数吗? msdn.microsoft.com/en-us/library/ms648075
  • 这是一个实用的工作想法,但让我害怕它会在未来崩溃(感谢 MSDN!)
  • @WarrenP,非常好的代码更新! :) 我只是在寻找这样的东西。顺便说一句,我相信DestroyIcon 是不需要的,因为Icon.Free 将通过内部FImage: TIconImage 处理它。
  • @WarrenP 您通过编译器开关选择使用 ANSI 或 WIDE 函数调用,但随后您必须使用 PChar 而不是 PWideChar ;o) 并将 hUserDLL 移动到实现部分以使其单元全局而不是对所有人都是全局的,只是为了减少副作用

标签: delphi resources icons


【解决方案1】:

PrivateExtractIcons 函数可以帮助您。您可以指定要提取的图标的大小:

{$IFDEF UNICODE}
    function PrivateExtractIcons(lpszFile: PChar; nIconIndex, cxIcon, cyIcon: integer; phicon: PHANDLE; piconid: PDWORD; nicon, flags: DWORD): DWORD; stdcall ; external 'user32.dll' name 'PrivateExtractIconsW';
{$ELSE}
    function PrivateExtractIcons(lpszFile: PChar; nIconIndex, cxIcon, cyIcon: integer; phicon: PHANDLE; piconid: PDWORD; nicon, flags: DWORD): DWORD; stdcall ; external 'user32.dll' name 'PrivateExtractIconsA';
{$ENDIF}

procedure TMainForm.Button1Click(Sender: TObject);
var
    hIcon   : THandle;
    nIconId : DWORD;
    Icon    : TIcon;
begin
    //Extract a 128x128 icon
    if PrivateExtractIcons ('C:\Users\Public\Documents\RAD Studio\Projects\2010\Aero Colorizer\AeroColorizer.exe', 0, 128, 128, @hIcon, @nIconId, 1, LR_LOADFROMFILE) <>0 then
    try
        Icon:=TIcon.Create;
        try
            Icon.Handle:=hIcon;
            Icon.SaveToFile(ExtractFilePath(ParamStr(0))+'Aicon.ico');
        finally
            Icon.Free;
        end;
    finally
        DestroyIcon (hIcon);
    end;
end ;

【讨论】:

  • 我会小心,因为页面顶部的警告。 "[此功能不适用于一般用途。在 Windows 的后续版本中可能会更改或不可用。]"
  • @Bruce,是的,你是对的,但我个人使用这个功能没有问题,从 Windows XP 到 Windows 7。
  • 哎哟。这是 MSDN 中的一个可怕警告。
  • 对不起,Warren,但这是我知道的除 ExtractIconEx 和 ExtractIcon 之外唯一可以提取图标的 Windows 功能。
  • @Bruce, @Warren - 但相对于其他解决方案而言,我更喜欢它的简单性。恕我直言,仍然会有许多功能被 MS 表示为已过时,并且由于这种向后兼容性而现在可以使用。但谁知道..
【解决方案2】:

这是working example on Delphi Praxis。它读取指定可执行文件的默认 RT_GROUP_ICON 和关联的 RT_ICON 资源,并将它们保存为完整的多图像 .ICO 文件。

至少在 XP 上,它似乎被 256 像素图像(以无效格式保存)弄糊涂了,因此可能需要稍作调整。

【讨论】:

  • 如果 XP-256x256 故障可以修复,看起来它可以提供原始解决方案。
  • 256px 的图像是 PNG 文件而不是位图。它们很特别,因为它们在图标结构中的高度和宽度值 = 0!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-10-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-02
  • 1970-01-01
  • 2021-01-31
相关资源
最近更新 更多