通过代码实现EXE文件图标的替换

原文:点击打开链接

通过代码实现EXE文件图标的替换

  最近,好多人问我如何通过写个小程序,动态替换可执行文件的图标。这个问题看起来虽小,但却涉及到很多问题。网上也只能找到一些零零散散的资料,却没有详细的指导性文档。所以我决定把这个问题写下来,以方便大家查阅。
  EXE文件图标的替换有很多方法,例如用一个EXE文件的图标替换另外一个EXE文件的图标;用一个ICO文件内的图标替换EXE文件的图标。这两种情况替换的方法不太相同,下面会详细讨论。
  EXE文件图标的替换更一般的情形,是PE(Portable Executable)文件图标的替换。只不过Windows操作系统只会显示EXE文件的图标罢了。但DLL、OCX等PE文件也都可以包含图标资源。下面我们从ICO文件格式说起,一步步讲解替换EXE文件图标的方法和原理。

.ico文件中图标的保存格式

  对于一个扩展名是.ico的文件,大部分人会认为一个ICO文件里面只能包含一个图标。但事实上,一个ICO文件里面可以包含很多图标。而且,目前大部分ICO文件里面都包含有不同尺寸、不同色深的好几个图标。我们以MSN安装包里的msnmsn.ico为例,这个图标文件就包含了9个不同尺寸、不同色深的图标,如图所示:


  这样做的目的,是为了保证不同的操作系统、不同的桌面色深,图标显示均可达到最佳效果。操作系统会选择并显示一个最合适的图标。Windows XP支持32位色的图标,Windows 2000最多只支持256色的图标。所以,如果我们开发的软件若要同时支持Windows XP和2000,那么为了达到视觉上的最佳效果,每一个ICO文件应至少包含两个图标,一个是32位色的,一个是256色的。 ICO文件头部结构定义如下:

typedef struct
{
    WORD           idReserved;   // Reserved (must be 0)
    WORD           idType;       // Resource Type (1 for icons)
    WORD           idCount;      // How many images?
    ICONDIRENTRY   idEntries[1]; // An entry for each image (idCount of 'em)
} ICONDIR, *LPICONDIR;

idCount表示该ICO文件包含图标的数量,所以理论上,一个ICO文件最多可以包含65535个图标。接下来,是该文件所包含的每一个图标的描述。

typedef struct
{
    BYTE        bWidth;          // Width, in pixels, of the image
    BYTE        bHeight;         // Height, in pixels, of the image
    BYTE        bColorCount;     // Number of colors in image (0 if >=8bpp)
    BYTE        bReserved;       // Reserved ( must be 0)
    WORD        wPlanes;         // Color Planes
    WORD        wBitCount;       // Bits per pixel
    DWORD       dwBytesInRes;    // How many bytes in this resource?
    DWORD       dwImageOffset;   // Where in the file is this image?
} ICONDIRENTRY, *LPICONDIRENTRY;

ICONDIRENTRY中记录了每一个图标的尺寸、色深、图标资源占用的字节数。dwImageOffset是一个文件偏移地址,指向图标资源数据起始位置。至于每一个图标资源内部的具体格式,与本文关系不大,这里就不再详细介绍了。

PE文件中的图标保存格式

  PE文件中的图标保存格式与.ico文件中图标的保存格式略有不同。PE文件中,把ICONDIR和图标资源作为两种资源类型分别保存,前者是RT_GROUP_ICON类型,后者是RT_ICON类型。为了与.ico文件中图标的保存格式做以区分,我们把PE文件中的图标保存格式重新定义如下:

// #pragmas are used here to insure that the structure's
// packing in memory matches the packing of the EXE or DLL.
#pragma pack( push )
#pragma pack( 2 )
typedef struct 
{
   WORD              idReserved;   // Reserved (must be 0)
   WORD              idType;       // Resource type (1 for icons)
   WORD              idount;       // How many images?
   GRPICONDIRENTRY   idEntries[1]; // The entries for each image
} GRPICONDIR, *LPGRPICONDIR;

typedef struct
{
   BYTE    bWidth;               // Width, in pixels, of the image
   BYTE    bHeight;              // Height, in pixels, of the image
   BYTE    bColorCount;          // Number of colors in image (0 if >=8bpp)
   BYTE    bReserved;            // Reserved
   WORD    wPlanes;              // Color Planes
   WORD    wBitCount;            // Bits per pixel
   DWORD   dwBytesInRes;         // how many bytes in this resource?
   WORD    nID;                  // the ID
} GRPICONDIRENTRY, *LPGRPICONDIRENTRY;
#pragma pack( pop )

这里有一个区别,就是在.ico文件中,ICONDIRENTRY结构最后一个成员dwImageOffset表示的是图标资源文件偏移地址。而PE文件中,GRPICONDIRENTRY结构最后一个成员nID表示的是图标的索引ID。

Windows API

  Windows操作系统为我们提供了几个API函数,用来更新PE文件中资源的函数有:BeginUpdateResource, UpdateResource, EndUpdateResource。用来枚举PE文件中资源的函数有:EnumResourceTypes,EnumResourceNames,EnumResourceLanguages。具体的使用方法可以参见MSDN。

下面我们通过具体的例子,来验证上面的方案是否可行。

用一个EXE中的图标替换另外一个EXE文件的图标

在这个例子中,我们用Windows XP自带的记事本的图标替换计算器的图标。


图2 记事本图标


图3 计算器图标

下面代码演示了如何替换32x32 32bits的图标:

HMODULE hModule = ::LoadLibrary("notepad.exe");
HRSRC hResInfo = ::FindResource(hModule, MAKEINTRESOURCE(8), RT_ICON);
HGLOBAL hGlobal = ::LoadResource(hModule, hResInfo);
DWORD dwSize = ::SizeofResource(hModule, hResInfo);
void* pData = ::LockResource(hGlobal);

HANDLE hUpdate = ::BeginUpdateResource("calc.exe", FALSE);
VERIFY(::UpdateResource(hUpdate, RT_ICON, MAKEINTRESOURCE(7), 
                        MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT),
                        pData, dwSize));
VERIFY(::EndUpdateResource(hUpdate, FALSE));
VERIFY(::FreeLibrary(hModule));

大家肯定有个疑问,上面代 MAKEINTRESOURCE(8)和 MAKEINTRESOURCE(7)是怎么来的呢?其实索引8和7分别是notepad.exe和calc.exe中,32x32 32bits图标的索引。我们可以通过加载RT_GROUP_ICON资源,然后遍历GRPICONDIRENTRY中每一个图标的大小、色深,找到这个图标的索引。为了简便,这里直接写死的索引号,省略了这一动态查找的过程。

还有一个疑问应该就MAKELANGID(LANG_ENGLISHSUBLANG_DEFAULT)了。PE文件中,每一个资源都至少对应一种语言。因为我的操作系统是英文的,所以记事本和计算器中的图标资源语言也是英文的。对于简体中文Windows XP操作系统所自带的记事本和计算器,这个值应该是MAKELANGID(LANG_CHINESE,SUBLANG_SYS_DEFAULT)。
那么我们怎么才能知道一个PE文件中,图标资源的语言是什么呢?我们可以通过资源枚举API,枚举所有图标、语言。可以参考上面提到过的那几个API函数,并查阅MSDN获取这些函数的帮助文档。 我们用记事本32x32 32bits图标替换计算器同样尺寸、色深的图标后,效果如下,在Titles显示方式下,图标大小是48x48的,图标没有被改变:


图4 48x48图标

在Icons显示方式下,图标大小是32x32的,图标被我们改变了:


图5 32x32图标

用一个ICO文件中的图标替换另外一个EXE文件的图标

  用ICO文件中的图标替换EXE文件图标稍微有点麻烦,我们必须借助数据结构ICONDIR和ICONDIRENTRY来完成。我们使用msnms.ico中的32x32 32bits图标替换计算器中同样大小色深的图标:

DWORD dwSize = sizeof(ICONDIRENTRY);

HANDLE hFile = ::CreateFile("msnms.ico", GENERIC_READ, FILE_SHARE_READ,
                            NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
::SetFilePointer(hFile, sizeof(ICONDIR) + dwSize * 6, NULL, FILE_BEGIN);

DWORD dwRead = 0;
ICONDIRENTRY Entry;
VERIFY(::ReadFile(hFile, &Entry, dwSize, &dwRead, NULL));

::SetFilePointer(hFile, Entry.dwImageOffset, NULL, FILE_BEGIN);

void* pData = new char[Entry.dwImageOffset];
VERIFY(::ReadFile(hFile, pData, Entry.dwBytesInRes, &dwRead, NULL));

HANDLE hUpdate = ::BeginUpdateResource("calc.exe", FALSE);
VERIFY(::UpdateResource(hUpdate, RT_ICON, MAKEINTRESOURCE(7),
                        MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT),
                        pData, Entry.dwBytesInRes));
VERIFY(::EndUpdateResource(hUpdate, FALSE));

delete[] pData;
pData = NULL;

VERIFY(::CloseHandle(hFile));

上面代码中sizeof(ICONDIR) + dwSize * 6的意思是定位到第8个标结构体ICONDIRENTRY的位置,这个图标是32x32 32bits的。我们可以通过遍历每一个ICONDIRENTRY来判断,到底哪个图标是这个尺寸的。这里我们为了简便,把这部分代码省略了。   定位到第8个图标结构体ICONDIRENTRY的位置后,Entry.dwImageOffset的值就是第8个图标资源的文件偏移地址,Entry.dwBytesInRes的值是第8个图标图标资源的大小。然后我们将文件指针定位到Entry.dwImageOffset,并读取Entry.dwBytesInRes大小的数据到指针pData指向的内存当中。 最后,是替换文件图标资源的代码,这部分代码跟上一个例子是相同的。

  本文抛砖引玉,介绍了EXE文件图标的替换,但完全可以推广到所有PE文件图标的替换(包括EXE、DLL等),也可推广到所有PE文件资源的替换(包括图标、图片、文字资源、对话框模板、菜单等)。可供相关人员参考。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: exe图标替换工具源码,其中exe代表“可执行文件”。这个工具源码的作用就是用于替换可执行文件图标。随着计算机应用的广泛,各种软件的可执行文件图标显得很重要,因为它是软件产品的标识之一。 执行源码的方法可以分为两种。一是使用现有工具直接打开执行;一是使用编译软件将源码编译成程序然后执行。 这个工具源码的实现理论比较简单,但实现上需要注意一些细节。具体来说: - 首先,需要通过系统调用定位到要修改的可执行文件; - 其次,需要解析可执行文件的资源表(resource table); - 之后,检查存在的图标是否满足替换条件(例如图标大小、颜色等); - 最后,将要替换图标写入可执行文件的资源表中。 以上步骤,最为关键的是解析资源表和写入图标数据。解析资源表需要根据可执行文件不同的格式进行不同的解析方式,需要针对不同的格式进行优化;写入图标数据时,需要非常小心防止出现数据格式错误等问题,需要进行一定的校验和错误检查。 总之,exe图标替换工具源码是一个比较实用的工具,其实现虽然简单,但要充分考虑到各种情况,因此考验编程人员的技术。 ### 回答2: exe 图标替换工具源码,首先需要了解 exe 文件的组成结构。exe 文件是一种可执行文件,通常由多个部分组成,包括头部信息、代码段、数据段、资源段等。其中,资源段包括了 exe 文件中所有的资源信息,如图标、菜单、对话框、字符串等。因此,修改 exe 文件图标,实际上就是修改 exe 文件中的资源段信息。 为了实现 exe 图标替换的功能,可以使用 Windows API 函数来操作 exe 文件。首先需要用到的是 LoadLibrary 和 FindResource 函数,分别用于加载当前进程中的 exe 文件以及查找资源。接着可以使用 LockResource 和 SizeofResource 函数来获取资源的位置和大小。最后需要用 UpdateResource 函数来修改资源信息,并保存更新后的 exe 文件。 以上是大致的流程,具体实现时需要考虑一些细节问题,比如如何找到 exe 文件、如何确定要替换图标资源等。同时,需要注意的是,替换 exe 图标的操作会影响 exe 文件的数字签名,因此需要重新进行数字签名以保证 exe 文件的安全性。 综上所述,exe 图标替换工具源码的实现需要使用 Windows API 函数,并考虑到一些细节问题和安全性问题。同时,需要了解 exe 文件的组成结构以及资源段的相关知识。 ### 回答3: exe 图标替换工具源码是一个将可执行文件图标替换为自定义图标程序。它可以帮助您在 Windows 操作系统中修改应用程序图标,使其更加符合您的个人喜好或需要。 该工具的源码通常使用 C# 或 VB.NET 编写,因为这些编程语言可以直接访问 Windows API,进而实现更多的功能。具体实现流程如下: 首先,程序会遍历指定目录下的所有可执行文件,并读取它们的图标信息。这个过程通常使用 Windows API 中的 FindFirstFile 和 FindNextFile 函数实现。 接着,程序会使用 Windows API 中的 ExtractIconEx 函数提取自定义的图标文件中的图标,并将其保存到内存中。如果用户没有提供自定义图标,则使用默认图标。 最后,程序会使用 Windows API 中的 UpdateResource 函数将自定义图标替换可执行文件中的图标资源。如果替换成功,程序将会输出日志并提示用户操作成功。 整个替换过程相对简单,但其中涉及到的 Windows API 接口较多,需要有一定的编程经验和操作系统知识。如果您是一名程序员,可以尝试理解并修改源码来使其更加适合您的需求;如果您只是一般用户,建议慎重使用该工具,以免误操作导致程序无法正常运行。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值