前言:这两天为exe文件做一个随机图标的功能,要求每次运行后图标都动态改变,在网上找了很多代码,都有一部分缺陷,参考了一些文档后进行了修改,现在在此进行总结:
一个icon资源(可以是*.ico文件,也可以是windows资源节区里的icon group),可以包含多张图片。这些图片有着各自的size或者颜色深度,这些图片可以是bmp格式或者png格式(vista之后支持,一般256*256时使用)
****************如果我们在win32环境下打开一个*.ico文件**************************
它的结构有些微改变,如下:
本文参考:http://blog.csdn.net/hbxtlhx/article/details/5026725
一个icon资源(可以是*.ico文件,也可以是windows资源节区里的icon group),可以包含多张图片。这些图片有着各自的size或者颜色深度,这些图片可以是bmp格式或者png格式(vista之后支持,一般256*256时使用)
****************如果我们在win32环境下打开一个*.ico文件**************************
Icon文件结构由两部分组成:icon文件头和多张图片数据,图片可以是bmp、png:
Icon文件头
typedef struct
{
WORD idReserved; //必须为0
WORD idType; //如果是icons则必须是1
WORD idCount; //表示有多少张图片
} ICONDIR, *LPICONDIR;
其中idReserved必须是0;idType对于ICON文件来说必须是1;idCount指明icon文件有多少张图片,也就指明了接下来有多少个ICONDIRENTRY结构体数据。
//在文件头后面则是 idCount个结构体 ICONDIRENTRY idEntries[x];
每张图片详情结构体,图片数据不在这其中
typedef struct
{
BYTE bWidth;
BYTE bHeight;
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;
//下面例子为从一个*.ico文件中更改exe文件的图标
void changedExeIcon(LPCTSTR lpExeName, LPCTSTR lpIconFile)
{
LPICONDIRENTRY pIconDirEntry(NULL);
LPGRPICONDIR pGrpIconDir(NULL);
HEADER header;
LPBYTE pIconBytes(NULL);
HANDLE hIconFile(NULL);
DWORD dwRet(0), nSize(0), nGSize(0), dwReserved(0);
HANDLE hUpdate(NULL);
BOOL ret(FALSE);
WORD i(0);
//打开图标文件
hIconFile = CreateFile(lpIconFile, GENERIC_READ, NULL, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hIconFile == INVALID_HANDLE_VALUE)
{
return;
}
//读取文件头部信息
ret = ReadFile(hIconFile, &header, sizeof(HEADER), &dwReserved, NULL);
if (!ret)
{
CloseHandle(hIconFile);
return;
}
//建立每一个图标的目录信息存放区域
pIconDirEntry = (LPICONDIRENTRY)new BYTE[header.idCount*sizeof(ICONDIRENTRY)];
if (pIconDirEntry == NULL)
{
CloseHandle(hIconFile);
return;
}
//从Icon文件中读取每一个图标的目录信息
ret = ReadFile(hIconFile, pIconDirEntry, header.idCount*sizeof(ICONDIRENTRY), &dwReserved, NULL);
if (!ret)
{
delete[] pIconDirEntry;
CloseHandle(hIconFile);
return;
}
//建立EXE文件中RT_GROUP_ICON所需的数据结构存放区域
nGSize = sizeof(GRPICONDIR) + header.idCount*sizeof(ICONDIRENTRY);
pGrpIconDir = (LPGRPICONDIR)new BYTE[nGSize];
//填充信息,这里相当于一个转换的过程
pGrpIconDir->idReserved = header.idReserved;
pGrpIconDir->idType = header.idType;
pGrpIconDir->idCount = header.idCount;
//复制信息并设置每一个图标对应的ID。ID为位置索引号
for (i = 0; i < header.idCount; i++)
{
pGrpIconDir->idEntries[i].bWidth = pIconDirEntry[i].bWidth;
pGrpIconDir->idEntries[i].bHeight = pIconDirEntry[i].bHeight;
pGrpIconDir->idEntries[i].bColorCount = pIconDirEntry[i].bColorCount;
pGrpIconDir->idEntries[i].bReserved = pIconDirEntry[i].bReserved;
pGrpIconDir->idEntries[i].wPlanes = pIconDirEntry[i].wPlanes;
pGrpIconDir->idEntries[i].wBitCount = pIconDirEntry[i].wBitCount;
pGrpIconDir->idEntries[i].dwBytesInRes = pIconDirEntry[i].dwBytesInRes;
pGrpIconDir->idEntries[i].nID = i;
}
//开始更新EXE中的图标资源,ID定为最小0,如果原来存在0ID的图标信息则被替换为新的。
hUpdate = BeginUpdateResource(lpExeName, false);
if (hUpdate != NULL)
{
//首先更新RT_GROUP_ICON信息
ret = UpdateResource(hUpdate, RT_GROUP_ICON, MAKEINTRESOURCE(128), MAKELANGID(LANG_CHINESE, SUBLANG_SYS_DEFAULT), (LPVOID)pGrpIconDir, nGSize);
if (!ret)
{
delete[] pIconDirEntry;
delete[] pGrpIconDir;
CloseHandle(hIconFile);
return;
}
//接着的是每一个Icon的信息存放
for (i = 0; i < header.idCount; i++)
{
//Icon的字节数
nSize = pIconDirEntry[i].dwBytesInRes;
//偏移文件的指针到当前图标的开始处
dwRet = SetFilePointer(hIconFile, pIconDirEntry[i].dwImageOffset, NULL, FILE_BEGIN);
if (dwRet == INVALID_SET_FILE_POINTER)
{
break;
}
//准备pIconBytes来存放文件里的Byte信息用于更新到EXE中。
delete[] pIconBytes;
pIconBytes = new BYTE[nSize];
ret = ReadFile(hIconFile, (LPVOID)pIconBytes, nSize, &dwReserved, NULL);
if (!ret)
{
break;
}
//更新每一个ID对应的RT_ICON信息
ret = UpdateResource(hUpdate, RT_ICON, MAKEINTRESOURCE(pGrpIconDir->idEntries[i].nID), MAKELANGID(LANG_CHINESE, SUBLANG_SYS_DEFAULT), (LPVOID)pIconBytes, nSize);
if (!ret)
{
break;
}
}
//结束EXE资源的更新操作
if (pIconBytes != NULL)
{
delete[] pIconBytes;
}
EndUpdateResource(hUpdate, false);
}
//清理资源并关闭Icon文件,到此更新操作结束!
delete[] pGrpIconDir;
delete[] pIconDirEntry;
CloseHandle(hIconFile);
}
它的结构有些微改变,如下:
注意:g_hModule为本进程实例句柄,也可以通过g_hModule = LoadLibrary(_TEXT("C:\\Ohter.exe")); 来获取其他exe中的资源文件,达到复制图标的效果。
//在使用时,这儿需要重新设置结构体对其方式,不然系统会默认为4个字节,我们就不能正确读取资源文件了
#pragma pack( push )
#pragma pack( 2 ) //我们调整为2个字节对其
typedef struct tagGRPICONDIR
{
WORD idReserved;
WORD idType;
WORD idCount;
GRPICONDIRENTRY idEntries[1];
}GRPICONDIR, *LPGRPICONDIR;
#pragma pack( pop ) //恢复对齐状态
#pragma pack( push )
#pragma pack( 2 )
typedef struct tagGRPICONDIRENTRY
{
BYTE bWidth;
BYTE bHeight;
BYTE bColorCount;
BYTE bReserved;
WORD wPlanes;
WORD wBitCount;
DWORD dwBytesInRes;
WORD nID;
}GRPICONDIRENTRY, *LPGRPICONDIRENTRY;;
#pragma pack( pop )
//下面例子为从一个Icon资源文件中更改exe文件的图标
void changedExeIcon(LPCTSTR lpExeName, UINT ulIconID)
{
HRSRC hRsrc = NULL;
HGLOBAL hGlobal = NULL;
HANDLE hUpdateRes = NULL;
LPVOID lpRes = NULL;
LPGRPICONDIR lpIcon;
if ((hRsrc = FindResource(g_hModule, MAKEINTRESOURCE(ulIconID), RT_GROUP_ICON)) == NULL)//获取ID为ulIconID1的Icon图标资源
return ;
if ((hGlobal = LoadResource(g_hModule, hRsrc)) == NULL)
return ;
if ((lpIcon = (LPGRPICONDIR)LockResource(hGlobal)) == NULL)
return ;
hUpdateRes = BeginUpdateResource(lpExeName, FALSE);
if (hUpdateRes == NULL)
return;
DWORD szRes = SizeofResource(g_hModule, hRsrc);
//128是WINDOWS系统默认图标
//更新exe的RT_GROUP_ICON
BOOL bSuc = UpdateResource(hUpdateRes, RT_GROUP_ICON, MAKEINTRESOURCE(128), MAKELANGID(LANG_CHINESE, SUBLANG_SYS_DEFAULT), LockResource(hGlobal), szRes);
//读取Icon中每一个图片
for (size_t i = 0; i < lpIcon->idCount; i++)
{
if ((hRsrc = FindResource(g_hModule, MAKEINTRESOURCE(lpIcon->idEntries[i].nID), RT_ICON)) == NULL)
{
return;
}
if ((hGlobal = LoadResource(g_hModule, hRsrc)) == NULL)
{
return;
}
szRes = SizeofResource(g_hModule, hRsrc);
//更新exe相应的RT_ICON
UpdateResource(hUpdateRes, RT_ICON, MAKEINTRESOURCE(i), MAKELANGID(LANG_CHINESE, SUBLANG_SYS_DEFAULT), LockResource(hGlobal), szRes);
//这儿有个疑问,在DLL中修改exe则为下面这样的格式,不知道为什么?
//UpdateResource(hUpdateRes, RT_ICON, MAKEINTRESOURCE(lpIcon->idEntries[i].nID), MAKELANGID(LANG_CHINESE, SUBLANG_SYS_DEFAULT), LockResource(hGlobal), szRes);
}
EndUpdateResource(hUpdateRes, FALSE);
}
本文参考:http://blog.csdn.net/hbxtlhx/article/details/5026725