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

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

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

    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 <> 0 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. for I := 0 to Ico.IconDir.idCount - 1 do
    22. begin
    23. CopyMemory(@GroupIconDir^.idEntries[I], @Ico.IconDir^.idEntries[I],
    24. SizeOf(TGroupIconDirEntry));
    25. // 索引从1开始
    26. GroupIconDir^.idEntries[I].nID := I + 1;
    27. // 写入图标数据
    28. SetLength(Data, Ico.IconDir^.idEntries[I].dwBytesInRes);
    29. Ico.IconStream.Position := Ico.IconDir^.idEntries[I].dwImageOffset;
    30. Ico.IconStream.ReadBuffer(Data[0], Length(Data));
    31. UpdateResource(hRes, RT_ICON, MakeIntResource(I + 1), 0, @Data[0], Length(Data));
    32. end;
    33. // 写入 RT_GROUP_ICON
    34. UpdateResource(hRes, RT_GROUP_ICON, MakeIntResource('MAINICON'), 0, GroupIconDir, GroupIconDirSize);
    35. FreeMem(GroupIconDir);
    36. FreeAndNil(Ico);
    37. EndUpdateResource(hRes, False);
    38. Result := True;
    39. end;
    40. end;

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

动态修改PE文件图标(使用UpdateResource API函数)的更多相关文章

  1. 替换应用程序exe图标,主要使用BeginUpdateResource,UpdateResource API函数

    替换应用程序exe图标,主要使用的API函数是BeginUpdateResource(),UpdateResource(),EndUpdateResource()来使用自定义的ico文件类替换exe程 ...

  2. 修改PE文件的入口函数OEP

    修改入口函数地址.这个是最省事的办法,在原PE文件中新增加一个节,计算新节的RVA,然后修改入口代码,使其指向新增加的节.当然,如果.text节空隙足够大的话,不用添加新节也可以. BOOL Chan ...

  3. maven 根据P参数值打包动态修改properties文件中值或一定properties

    需求:由于最近开发clover项目 ,没有使用spring,更没有使用任何框架,而使用J2EE的web工程,所以连接ZK和MongoDB.Redis等服务器需用指定properties文件, 而目前公 ...

  4. Dll注入:修改PE文件 IAT注入

    PE原理就不阐述了, 这个注入是PE感染的一种,通过添加一个新节注入,会改变PE文件的大小,将原有的导入表复制到新节中,并添加自己的导入表描述符,最后将数据目录项中指向的导入表的入口指向新节. 步骤: ...

  5. 用UpdateResource修改EXE文件图标(已修正)

    //请自行添加到 Type 处PICONDIRENTRY = ^ICONDIRENTRY;ICONDIRENTRY = packed record bWidth: Byte; bHeight: Byt ...

  6. 动态修改css文件中,具体的class中的个别属性值。

    function setStyleSheetObjCssClassProperty(pStyleSheetObj, pSelectorText, pProperty, pValue) { var pS ...

  7. BS Web窗体 动态修改WebConfig文件参数及数据库链接串

    WebConfig操作帮助类 /// /// ConfigurationOperator 的摘要说明 /// public class ConfigurationOperator : IDisposa ...

  8. 应用程序加载外部字体文件(使用AddFontResource API函数指定字体)

    /* MSDN: Any application that adds or removes fonts from the system font table should notify other w ...

  9. 打开并锁定一个文件(使用LockFile API函数)

    var aHandle : THandle; aFileSize : Integer; aFileName : String; procedure TForm1.Button3Click(Sender ...

随机推荐

  1. 【行业干货】2013中国零售商排名 - 课程公告板 - 京东内部论坛 - Powered by Discuz!

    [行业干货]2013中国零售商排名 - 课程公告板 - 京东内部论坛 - Powered by Discuz! [行业干货]2013中国零售商排名 [复制链接]     bjpanzhoulan   ...

  2. CSipSimple最新版本号(二)--加入视频功能

    前面我们编译好了最新版本号的CSipSimple,并且測试已经能够打电话了.如今要把视频功能加上去. 不知道怎么编译的,能够看我的上一篇博文:CSipSimple最新版本号 我们先来看一下之前的项目是 ...

  3. 正态分布(Normal distribution)又名高斯分布(Gaussian distribution)

    正态分布(Normal distribution)又名高斯分布(Gaussian distribution),是一个在数学.物理及project等领域都很重要的概率分布,在统计学的很多方面有着重大的影 ...

  4. MSSQL奇技淫巧

    MSSQL:获得库每个表的记录数和容量 sp_msforeachtable是MS未公开的存储过程: exec sp_msforeachtable @command1="print '?'&q ...

  5. 04-Foundation-NSSet、NSDictionary、block

    目录: 一.NSSet集合 二.NSDictionary字典 三.block代码块 回到顶部 一.NSSet集合 1 NSSet是一个无序的,管理对个对象的集合类,最大特点是集合中不允许出现重复对象, ...

  6. Jquery学习笔记:通过层次关系获取jquery对象

    前面一篇文章,我们介绍了如何通过web标签的id , css样式值来获取jquery对象. 但这只是基本方法,不能满足所有场景的需求. 本文介绍通过dom元素之间的层次关系获取元素.具体是将各种标识符 ...

  7. 在 Perl看来, 字符串只有两种形式. 一种是octets, 即8位序列, 也就是我们通常说的字节数组. 另一种utf8编码的字符串, perl管它叫string. 也就是说: Perl只熟悉两种编

    在 Perl看来, 字符串只有两种形式. 一种是octets, 即8位序列, 也就是我们通常说的字节数组. 另一种utf8编码的字符串, perl管它叫string. 也就是说: Perl只熟悉两种编 ...

  8. 【linux】具体芯片MACHINE_START处理

    欢迎转载,转载时需保留作者信息,谢谢. 邮箱:tangzhongp@163.com 博客园地址:http://www.cnblogs.com/embedded-tzp Csdn博客地址:http:// ...

  9. Windows下用WinSCP传输数据到Linux上

    Scenario:最近公司做的一个项目,UI部分我是使用python在编译时做localization的,是linux下运行的,但是开发是在windows下进行的每次编译后都要手动通过WinSCP这个 ...

  10. 如何在myeclipse有个项目文件很多,我想找一段代码,怎么查找?

    然后输入要找的文字 然后在File name pathherns 中写 *.java 如果有多个就可以用逗号分隔! 然后 search