说明
EXE文件的ICON图标一般由数个图标组成,在更新ICON图标时需要逐一添加每个图标,再添加ICON_GROUP目录,可以使用ResourceHacker软件进行查看。
EXE文件中的图标数据结构和ICON图标文件结构很相似,唯一区别在于ICON图标的ICONDIRENTRY最后一个字段标注的是图像数据首地址,EXE文件的GRPICONDIRENTRY最后一个字段标注的是图标的ID。
代码
#include <windows.h>
#include <stdio.h>
#pragma pack(push, 2) //结构体需要双字节对齐
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;
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;
typedef struct
{
BITMAPINFOHEADER icHeader; // DIB header
RGBQUAD icColors[1]; // Color table
BYTE icXOR[1]; // DIB bits for XOR mask
BYTE icAND[1]; // DIB bits for AND mask
} ICONIMAGE;
typedef struct
{
WORD idReserved; // Reserved (must be 0)
WORD idType; // Resource type (1 for icons)
WORD idCount; // How many images?
// GRPICONDIRENTRY idEntries[1]; // The entries for each image
} GRPICONDIR;
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;
#pragma pack(pop)
int main(int argc, char *argv[]) {
// if (argc != 3) {
// printf("Usage: %s <target_exe> <new_icon.ico>\n", argv[0]);
// return 1;
// }
//
// const char *exePath = argv[1];
// const char *iconPath = argv[2];
char exePath[] = "pip.exe";
char iconPath[] = "app.ico";
// 打开图标文件
HANDLE hIcon = CreateFile(iconPath, GENERIC_READ, 0, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hIcon == INVALID_HANDLE_VALUE) {
printf("[%lu]Error opening icon file\n", GetLastError());
return 1;
}
// 获取图标文件大小
DWORD iconSize = GetFileSize(hIcon, NULL);
if (iconSize == INVALID_FILE_SIZE) {
printf("[%lu]Error getting icon file size\n", GetLastError());
CloseHandle(hIcon);
return 1;
}
// 读取图标文件
BYTE* iconData = malloc(iconSize);
DWORD bytesRead;
if (!ReadFile(hIcon, iconData, iconSize, &bytesRead, NULL) || bytesRead != iconSize) {
printf("[%lu]Error reading icon file\n", GetLastError());
free(iconData);
CloseHandle(hIcon);
return 1;
}
CloseHandle(hIcon);
// 解析图标文件目录
ICONDIR* iconDir = (ICONDIR*)iconData;
if (iconDir->idReserved != 0 || iconDir->idType != 1) {
printf("Invalid icon file format\n");
free(iconData);
return 1;
}
// 计算图标组资源大小
DWORD groupSize = sizeof(GRPICONDIR) + iconDir->idCount * sizeof(GRPICONDIRENTRY);
BYTE* groupData = malloc(groupSize);
GRPICONDIR* grpIconDir = (GRPICONDIR*)groupData;
grpIconDir->idReserved = 0;
grpIconDir->idType = 1; // RT_ICON
grpIconDir->idCount = iconDir->idCount;
// 创建图标组资源
ICONDIRENTRY* iconEntries = (ICONDIRENTRY*)(iconData + sizeof(ICONDIR));
GRPICONDIRENTRY* grpEntries = (GRPICONDIRENTRY*)(groupData + sizeof(GRPICONDIR));
for (int i = 0; i < iconDir->idCount; i++) {
grpEntries[i].bWidth = iconEntries[i].bWidth;
grpEntries[i].bHeight = iconEntries[i].bHeight;
grpEntries[i].bColorCount = iconEntries[i].bColorCount;
grpEntries[i].bReserved = iconEntries[i].bReserved;
grpEntries[i].wPlanes = iconEntries[i].wPlanes;
grpEntries[i].wBitCount = iconEntries[i].wBitCount;
grpEntries[i].dwBytesInRes = iconEntries[i].dwBytesInRes;
grpEntries[i].nID = i + 1; // 资源ID从1开始
}
// 开始更新EXE资源
HANDLE hUpdate = BeginUpdateResource(exePath, FALSE);
if (hUpdate == NULL) {
printf("BeginUpdateResource failed: %lu\n", GetLastError());
free(iconData);
free(groupData);
return 1;
}
// 删除现有的图标组资源
if (!UpdateResource(hUpdate, RT_GROUP_ICON, MAKEINTRESOURCE(101),
MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), NULL, 0)) {
printf("[%lu]Warning: Failed to remove old icon group ID-101\n", GetLastError());
}
// 删除现有的图标资源
for (int i = 1; i <= 7; i++) { // 删除ID 1-7的图标资源
if (!UpdateResource(hUpdate, RT_ICON, MAKEINTRESOURCE(i),
MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), NULL, 0)) {
printf("[%lu]Warning: Failed to remove old icon ID-%d\n", GetLastError(), i);
};
}
// 添加新的图标组资源
if (!UpdateResource(hUpdate, RT_GROUP_ICON, MAKEINTRESOURCE(101),
MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
groupData, groupSize)) {
printf("[%lu]UpdateResource (group icon) failed\n", GetLastError());
free(iconData);
free(groupData);
EndUpdateResource(hUpdate, TRUE);
return 1;
}
// 添加各个图标资源
for (int i = 0; i < iconDir->idCount; i++) {
DWORD offset = iconEntries[i].dwImageOffset;
DWORD size = iconEntries[i].dwBytesInRes;
if (offset + size > iconSize) {
printf("Icon data out of bounds: entry %d\n", i);
continue;
}
if (!UpdateResource(hUpdate, RT_ICON, MAKEINTRESOURCE(i + 1),
MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
iconData + offset, size)) {
printf("[%lu]UpdateResource (icon %d) failed\n", i, GetLastError());
}
}
// 完成资源更新
if (!EndUpdateResource(hUpdate, FALSE)) {
printf("[%lu]EndUpdateResource failed\n", GetLastError());
free(iconData);
free(groupData);
return 1;
}
printf("Icon group updated successfully!\n");
free(iconData);
free(groupData);
return 0;
}