动态修改PE文件图标(使用UpdateResource API函数)

PE文件的图标存储在资源文件中,而操作资源要用到的API函数就是UpdateResource
首先我们需要先了解一下ICO格式,参考资料:http://www.moon-soft.com/program/FORMAT/windows/icons.htm
ICO格式不复杂,就是由数据头、数据目录、数据三个部分组成

一个.ico文件中可能含有若干个图标,我们需要将数据目录和数据解析出来。简单地写了个单元

[delphi] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. unit Icons;  
  2.   
  3. interface  
  4.   
  5. uses  
  6.   Winapi.Windows, System.SysUtils, System.Classes;  
  7.   
  8. type  
  9.   
  10.   { 用于ICO图标文件 }  
  11.   
  12.   TIconDirEntry = packed record  
  13.     bWidth: Byte;  
  14.     bHeight: Byte;  
  15.     bColorCount: Byte;  
  16.     bReserved: Byte;  
  17.     wPlanes: Word;  
  18.     wBitCount: Word;  
  19.     dwBytesInRes: DWORD;  
  20.     dwImageOffset: DWORD;  
  21.   end;  
  22.   PIconDirEntry = ^TIconDirEntry;  
  23.   
  24.   TIconDir = packed record  
  25.     idReserved: Word;  
  26.     idType: Word;  
  27.     idCount: Word;  
  28.     idEntries: array [0..0] of TIconDirEntry;  
  29.   end;  
  30.   PIconDir = ^TIconDir;  
  31.   
  32.   { 用于PE文件中的图标 }  
  33.   
  34.   TGroupIconDirEntry = packed record  
  35.     bWidth: Byte;  
  36.     bHeight: Byte;  
  37.     bColorCount: Byte;  
  38.     bReserved: Byte;  
  39.     wPlanes: Word;  
  40.     wBitCount: Word;  
  41.     dwBytesInRes: DWORD;  
  42.     nID: Word;  
  43.   end;  
  44.   
  45.   TGroupIconDir = packed record  
  46.     idReserved: Word;  
  47.     idType: Word;  
  48.     idCount: Word;  
  49.     idEntries: array [0 .. 0] of TGroupIconDirEntry;  
  50.   end;  
  51.   PGroupIconDir = ^TGroupIconDir;  
  52.   
  53.   { ICO图标文件 }  
  54.   TIcoFile = class  
  55.   public  
  56.     IconStream: TMemoryStream;  
  57.     IconDir: PIconDir;  
  58.     IconDirSize: DWORD;  
  59.   
  60.     constructor Create; overload;  
  61.     constructor Create(const FileName: string); overload;  
  62.     destructor Destroy; override;  
  63.   
  64.     // 加载ICO数据  
  65.     procedure LoadFromFile(const FileName: string);  
  66.     procedure LoadFromStream(Stream: TStream);  
  67.   end;  
  68.   
  69. implementation  
  70.   
  71. { TIcoFile }  
  72.   
  73. constructor TIcoFile.Create;  
  74. begin  
  75.   IconStream := TMemoryStream.Create;  
  76.   IconDir := nil;  
  77.   IconDirSize := 0;  
  78. end;  
  79.   
  80. constructor TIcoFile.Create(const FileName: string);  
  81. begin  
  82.   Create;  
  83.   LoadFromFile(FileName);  
  84. end;  
  85.   
  86. destructor TIcoFile.Destroy;  
  87. begin  
  88.   FreeMem(IconDir);  
  89.   FreeAndNil(IconStream);  
  90.   inherited;  
  91. end;  
  92.   
  93. procedure TIcoFile.LoadFromFile(const FileName: string);  
  94. var  
  95.   MS: TMemoryStream;  
  96. begin  
  97.   MS := TMemoryStream.Create;  
  98.   try  
  99.     MS.LoadFromFile(FileName);  
  100.     LoadFromStream(MS);  
  101.   finally  
  102.     FreeAndNil(MS);  
  103.   end;  
  104. end;  
  105.   
  106. procedure TIcoFile.LoadFromStream(Stream: TStream);  
  107. var  
  108.   Dir: TIconDir;  
  109. begin  
  110.   Stream.Position := 0;  
  111.   IconStream.Clear;  
  112.   IconStream.CopyFrom(Stream, Stream.Size);  
  113.   
  114.   IconStream.Position := 0;  
  115.   IconStream.ReadBuffer(Dir, SizeOf(Dir));  
  116.   
  117.   FreeMem(IconDir);  
  118.   IconDirSize := SizeOf(TIconDirEntry) * (Dir.idCount - 1) + SizeOf(TIconDir);  
  119.   IconDir := AllocMem(IconDirSize);  
  120.   IconStream.Position := 0;  
  121.   IconStream.ReadBuffer(IconDir^, IconDirSize);  
  122. end;  
  123.   
  124. end.  


这里要注意一个问题,ICO文件中的TIconDirEntry结构和PE文件中的TGroupIconDirEntry结构是不同的
不同处在最后一个参数,ICO文件中表示的是图像数据偏移地址,是DWORD类型。而PE文件中表示的是图像数据的索引,是WORD类型,少了2个字节
替换图标需要写入2个部分:RT_GROUP_ICON 和 RT_ICON
RT_GROUP_ICON也就是ICO的数据头部分,RT_ICON就是图像数据部分了
数据部分直接写入即可,不用转换什么的。但是数据头需要稍微处理一下,因为前面说了,这个替换过程是把ICO文件写到PE文件中
而他们数据头部分结构略有不同(PE文件中每个数据头比起ICO数据头要少2字节),只用把这里处理下就行了
最后写个函数就可以替换PE文件图标了

[delphi] view plain copy
 
 在CODE上查看代码片派生到我的代码片
    1. function TMainForm.UpdatePeIcon(IcoFile, PeFile: string): Boolean;  
    2. var  
    3.   Ico: TIcoFile;  
    4.   I: Integer;  
    5.   hRes: THandle;  
    6.   GroupIconDir: PGroupIconDir;  
    7.   GroupIconDirSize: DWORD;  
    8.   Data: TBytes;  
    9. begin  
    10.   Result := False;  
    11.   hRes := BeginUpdateResource(PChar(PeFile), False);  
    12.   if hRes <> then  
    13.   begin  
    14.     Ico := TIcoFile.Create(IcoFile);  
    15.     // TGroupIconDirEntry结构要比TIconDirEntry结构少2字节  
    16.     GroupIconDirSize := Ico.IconDirSize - Ico.IconDir^.idCount * 2;  
    17.     GroupIconDir := AllocMem(GroupIconDirSize);  
    18.     GroupIconDir^.idReserved := 0;  
    19.     GroupIconDir^.idType := 1;  
    20.     GroupIconDir^.idCount := Ico.IconDir.idCount;  
    21.   
    22.     for I := to Ico.IconDir.idCount - do  
    23.     begin  
    24.       CopyMemory(@GroupIconDir^.idEntries[I], @Ico.IconDir^.idEntries[I],  
    25.         SizeOf(TGroupIconDirEntry));  
    26.       // 索引从1开始  
    27.       GroupIconDir^.idEntries[I].nID := I + 1;  
    28.   
    29.       // 写入图标数据  
    30.       SetLength(Data, Ico.IconDir^.idEntries[I].dwBytesInRes);  
    31.       Ico.IconStream.Position := Ico.IconDir^.idEntries[I].dwImageOffset;  
    32.       Ico.IconStream.ReadBuffer(Data[0], Length(Data));  
    33.   
    34.       UpdateResource(hRes, RT_ICON, MakeIntResource(I + 1), 0, @Data[0], Length(Data));  
    35.     end;  
    36.   
    37.     // 写入 RT_GROUP_ICON  
    38.     UpdateResource(hRes, RT_GROUP_ICON, MakeIntResource('MAINICON'), 0, GroupIconDir, GroupIconDirSize);  
    39.   
    40.     FreeMem(GroupIconDir);  
    41.     FreeAndNil(Ico);  
    42.     EndUpdateResource(hRes, False);  
    43.     Result := True;  
    44.   end;  
    45. end;  

http://blog.csdn.net/aqtata/article/details/7710720

原文地址:https://www.cnblogs.com/findumars/p/5277414.html