简体   繁体   English

我正在尝试对 Windows 可执行文件的图标进行资源更新,我快到了,我做错了什么?

[英]I'm trying to do a resource update on the icons of a Windows executable and I'm nearly there, what am I doing wrong?

Here's a fresh version of my code.这是我的代码的新版本。 It is now even closer, if I look at the updated version in Resource Hacker it tells me that the group has nine icons, which is true, that they're 16.8 million color, which is true, that they're all 16 x 16, which is not true, and that it can't actually show me what they look like, which is annoying.现在更接近了,如果我查看 Resource Hacker 中的更新版本,它会告诉我该组有九个图标,这是真的,它们是 1680 万色,这是真的,它们都是 16 x 16 ,这不是真的,而且它实际上无法向我展示它们的外观,这很烦人。 Also that they all have an ordinal name of 150 if that means anything to anyone.此外,如果这对任何人都意味着什么,它们都有一个 150 的序号名称。

procedure TForm1.Button1Click(Sender: TObject);
var   vResHandle: THandle;
      MyIcon: TMemoryStream;
begin
  // Get the icon.
  MyIcon := TMemoryStream.Create;
  MyIcon.LoadFromFile('icon.ico');
  // Set the position in the memory stream to the start.
  MyIcon.Seek(0, soFromBeginning);

  // Get the handle.
  vResHandle := BeginUpdateResource('exec.exe', False);
  if vResHandle=0 then
    raise Exception.Create('System giving error message: '
                           + SysErrorMessage(GetLastError));
    try
    // Change the icon.
    if not UpdateResource(vResHandle
                          , RT_GROUP_ICON
                          , PChar('MAINICON')
                          , LANG_NEUTRAL
                          , MyIcon.Memory
                          , MyIcon.Size)
    then
      raise Exception.Create('System giving error message: '
                             + SysErrorMessage(GetLastError));
    finally
    EndUpdateResource(vResHandle, False);
    end;
  MyIcon.Free;
end;  

            

Short version of how it works: So.其工作原理的简短版本:所以。 Before you try to put any bit of data into an.exe file using a resource update you must be sure it will fit.在您尝试使用资源更新将任何数据放入 .exe 文件之前,您必须确保它适合。 Icon files are difficult.图标文件很难。 In this particular case I needed to modify the structure of the.ico file and split it into different pieces and do a resource update separately on each.在这种特殊情况下,我需要修改 .ico 文件的结构并将其拆分为不同的部分,并分别对每个部分进行资源更新。 I didn't do that.我没有那样做。 I was like someone trying to fit a seven-fingered hand into one finger of a five-fingered glove.我就像有人试图将七指手放入五指手套的一个手指中。

How the thing works is explained in the code but what exact effect it has on Windows must be explained up here.代码中解释了它的工作原理,但它对 Windows 的确切影响必须在此处进行解释。

(1) Although the application icon (in the top left corner of your main form) can be set to be completely different from the main icon for the program, it seems like it's overwritten to be in line with the main icon once you do the update. (1) 虽然应用程序图标(在主窗体的左上角)可以设置为与程序的主图标完全不同,但一旦你这样做,它似乎被覆盖为与主图标一致更新。 99% of the time this would be exactly what you want. 99% 的情况下,这正是您想要的。 If it isn't what you want you'll have to take it from here.如果这不是你想要的,你必须从这里拿走。

(2) File Explorer caches this stuff so hard that you won't see any change in how the icon's displayed there unless you restart Explorer. (2) 文件资源管理器缓存这些内容非常困难,除非您重新启动资源管理器,否则您不会看到图标在此处的显示方式有任何变化。 This is fine for my purposes, if it's a problem for you then again you'll have to solve it yourself, sorry.这对我来说很好,如果这对你来说是个问题,那么你必须自己解决它,对不起。

(3) This is NOT an answer to that frequently-asked question, "How do I change the toolbar icon of my Pascal-based application while it's running?" (3) 这不是对常见问题“如何在基于 Pascal 的应用程序运行时更改其工具栏图标?”的回答。 Because you can't do a resource update on an executable that's being executed, whether your own or another.因为您无法对正在执行的可执行文件进行资源更新,无论是您自己的还是其他人。

(4) If you're going to use this in Pascal, then you're going to need to add Windows to your uses statement. (4) 如果您要在 Pascal 中使用它,那么您需要将 Windows 添加到您的使用语句中。 If you're going to use this in any language in other than Pascal but you're still using Windows, then it will translate kind of easily because it's basically telling the Windows OS to do stuff, but you'll have to find out which library or whatever lets you do that and what syntax it wants you to use.如果你打算在 Pascal 以外的任何语言中使用它,但你仍在使用 Windows,那么它会很容易翻译,因为它基本上是在告诉 Windows 操作系统做一些事情,但你必须找出哪个图书馆或任何让你这样做的东西,以及它希望你使用什么语法。

(5) If you're wondering about how to do the thing the other way round and extract an.ico file from an executable file, then this is of course theoretically possible and has been done by cleverer people then me and indeed done in Pascal. (5) 如果您想知道如何反过来做并从可执行文件中提取 an.ico 文件,那么这在理论上当然是可能的,并且已经由比我更聪明的人完成了,并且确实在 Pascal 中完成了. (Download Resource Hacker for an example.) But you can't just do this by reversing my code as it were, there are obstacles in your way. (例如下载 Resource Hacker。)但是你不能仅仅通过反转我的代码来做到这一点,你的方式有障碍。 Doing it this way Windows has built in facilities for me to do this.这样做 Windows 已经为我建立了这样做的设施。 Doing it the other way it seems like it doesn't.以另一种方式做它似乎没有。

procedure TForm1.Button1Click(Sender: TObject);
var   vResHandle: THandle;
      MyIcon: TMemoryStream;
      i,j: integer;
      s: string;
      ImageCount: Word;
      ImageSize: DWord;
      ab, m: TMemoryStream;
 
const HeaderSize = 6;
      IcoEntrySize = 16;
      ResEntrySize = 14;
 
begin
 
// Short explanation. An icon file consists of (a) a six-byte header which
// includes among other things information about how many icons are in
// the file; (b) sixteen bytes of metadata for each icon; (c) the icons.
 
// But that's how icons are stored as files. As executable resources,
// Windows considers that (a) and (b) are one resource but (c) is a different
// resource, indeed one resource for each image, so we have to split the icon
// file up and do several resource updates.
 
// It also requires only fourteen bytes of metadata per entry: instead of the
// last parameter being a double word referring to the position of the image
// in memory, it's a single word conferring an ID.
 
// Initialize stuff
MyIcon := TMemoryStream.Create;
ab := TMemoryStream.Create;
m := TMemoryStream.Create;
 
// Get the icon
MyIcon.LoadFromFile('icon.ico');
 
// Get the handle for the resource update..
vResHandle := BeginUpdateResource('test.exe', False);
 
// We skip forward in the memory stream to where Windows keeps the image count and read it.
MyIcon.Seek(4,soFromBeginning);
ImageCount:=MyIcon.ReadWord;
 
// Go back to the beginning ...
MyIcon.Seek(0,soFromBeginning);
 
// We read the directory information into ab, modifying its format as we do so.
 
for j:=1 to HeaderSize do ab.WriteByte(MyIcon.ReadByte);
    for i:=1 to ImageCount do
        begin
        for j:=1 to IcoEntrySize - 4 do ab.WriteByte(MyIcon.ReadByte);
        MyIcon.ReadDWord;  // To skip over it.
        ab.WriteWord(i);
        end;
 
// Update the icon directory with ab, which is now in the correct format.
 
UpdateResource(vResHandle
                      , RT_GROUP_ICON
                      , PChar('MAINICON')
                      , LANG_NEUTRAL
                      , ab.Memory
                      , ab.Size);
 
// Now the size of each icon is contained as a double word in the directory
// entries for each item, so we use that to cut the remainder of the memory
// stream into chunks and update them one at a time.
 
for i := 1 to ImageCount do
    begin
    m := TMemoryStream.Create;
    ab.Seek(HeaderSize+(i-1)*ResEntrySize+8,soFromBeginning);
    ImageSize:=ab.ReadDWord;
    for j:=1 to ImageSize do m.WriteByte(MyIcon.ReadByte);
    UpdateResource(vResHandle
                  , RT_ICON
                  , MAKEINTRESOURCE(i)
                  , LANG_NEUTRAL
                  , m.Memory
                  , m.Size);
    m.Free;
    end;
 
EndUpDateResource(vResHandle,False);
MyIcon.Free;
ab.Free;
end;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM