最近,好多人问我如何通过写个小程序,动态替换可执行文件的图标。这个问题看起来虽小,但却涉及到很多问题。 上也只能找到一些零零散散的资料,却没有详细的指导性文档。所以我决定把这个问题写下来,以方便大家查阅。
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个不同尺寸、不同色深的图标,如图所示:
图表 2 记事本图标
图表 4 48×48图标
在Icons显示方式下,图标大小是32×32的,图标被我们改变了:

图表 5 32×32图标
五、用一个ICO文件中的图标替换另外一个EXE文件的图标
用ICO文件中的图标替换EXE文件图标稍微有点麻烦,我们必须借助数据结构ICONDIR和ICONDIRENTRY来完成。我们使用msnms.ico中的32×32 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的位置,这个图标是32×32 32bits的。我们可以通过遍历每一个ICONDIRENTRY来判断,到底哪个图标是这个尺寸的。这里我们为了简便,把这部分代码省略了。
定位到第8个图标结构体ICONDIRENTRY的位置后,Entry.dwImageOffset的值就是第8个图标资源的文件偏移地址,Entry.dwBytesInRes的值是第8个图标图标资源的大小。然后我们将文件指针定位到Entry.dwImageOffset,并读取Entry.dwBytesInRes大小的数据到指针pData指向的内存当中。
最后,是替换文件图标资源的代码,这部分代码跟上一个例子是相同的。
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!