如何更换可执行文件的图标_C++源码
VC 2008-09-26 21:39:41 阅读595 评论0 字号:大中小 订阅
翻译: vivi
日期: 2008-9-26
原文: http://www.codeguru.com/cpp/w-p/win32/tutorials/article.php/c12873__1/
环境: C++, WinAPI
偶的E文不好,翻译不对的地方,请见谅。如需转载请注明出处
http://afxvivi.blog.163.com/blog/static/93474001200882693941937/
导言
在这篇教程中,我将向你展示如何用一个给定的图标文件替换一个应用程序的图标。 为了简单化,你需要准备一个32x32的图标文件(同时,我将给出完成这些工作的完整源代码),我还会提供一般理论,来支持所有类型的图标,让您扩展例子。
周围有很多图标编辑器,用你喜欢的做一个32x32, 256色的图标。
事有先后: 先说图标文件在磁盘上的结构
图标文件结构如下:
ICON HEADER:
WORD Reserved - reserved
WORD Type - 1 means icon, 2 means cursor
WORD ImageCount - number of images stored inside the file
ICON DIRECTORY ENTRY - there are ImageCount directory entries
BYTE Width - width of image
BYTE Height - height of image
BYTE Colors - 0 means 256 or more
BYTE Reserved - reserved
WORD Planes - number of planes
WORD BitsPerPixel - bits per pixel
DWORD ImageSize - size of image data
DWORD ImageOffset - offset of image data inside file
ICON DIRECTORY ENTRY
...
ICON DIRECTORY ENTRY
ICON IMAGE - the actual image data (which has the
structure of ICON IMAGE a bitmap) found
at the location specified by ImageOffset
...
ICON IMAGE
正如您所看到的,一个图标文件可以包含一个以上的图像。
秘密
读取一个图标文件到内存中,并把它作为一个RC_ICON资源插入,因为由此产生的资源会被损坏。在资源中, 图标是被分成许多RC_ICON资源和一个RC_GROUP_ICON。一个RC_ICON入口基本上就是图标图像。RC_GROUP_ICON 包含ICON HEADER和 DIRECTORY ENTRIES ,还有一点改变: DWORD ImageOffset变成了WORD ResourceID。这是因为实际的图像数据已不再跟随其头部,因此可以用其资源ID来识别 。
一个应用程序可以在其资源中包含许多图标。通常,要显示的图标资源ID等于1,但也不是必然的。Windows通过一个叫做'MAINICON' 的RC_GROUP_ICON 来选择显示的图标。你可以用一个工具,例如PE Explorer,来看一看已编译的可执行模块的资源 (.EXE和.DLL文件)。
你要做的
把一个图标文件的ICON IMAGE 装入缓存,生成包含RC_GROUP_ICON资源的数据,然后把它们插入到一个给定的可执行文件中。
下面是一些结构声明:
// Make sure structures are packed the same way as Windows packs them
#pragma pack(push, 2)
typedef struct // This is the Directory Entry stored in resources
{
BYTE Width;
BYTE Height;
BYTE Colors;
BYTE Reserved;
WORD Planes;
WORD BitsPerPixel;
DWORD ImageSize;
WORD ResourceID
} IconDirResEntry, *PIconDirResEntry;
typedef struct // This is the actual RT_GROUP_ICON structure
{
WORD Reserved;
WORD ResourceType;
WORD ImageCount;
PIconDirResEntry Enries; // The number of entries is ImageCount
} GroupIcon;
// Restore default packing
#pragma pack(pop)
在这个例子中,你可以声明一个较简单的结构因为你用的图标文件只包含一幅图像。 你还可以把头部和目录项组合到一个简单的结构中。
#pragma pack(push, 2)
typedef struct {
WORD Reserved1; // reserved, must be 0
WORD ResourceType; // type is 1 for icons
WORD ImageCount; // number of icons in structure (1)
BYTE Width; // icon width (32)
BYTE Height; // icon height (32)
BYTE Colors; // colors (0 means more than 8 bits per pixel)
BYTE Reserved2; // reserved, must be 0
WORD Planes; // color planes
WORD BitsPerPixel; // bit depth
DWORD ImageSize; // size of structure
WORD ResourceID; // resource ID
} GROUPICON;
#pragma pack(pop)
最后一个结构是你将要插入到可执行文件的RC_GROUP_ICON 资源。
用WinAPI
你可以用WinAPI's BeginUpdateResource, UpdateResource, 和 EndUpdateResource来更新资源。 声明一个InjectMainIcon方法来接收图标文件和图标文件的文件名。
快速总览: BeginUpdateResource接收可执行模块的名字和一个布尔参数,如果这个布尔参数被设为真,将移除在可执行文件中找到的所有资源 (你不想那么做)。
UpdateResource接收用来更新模块的句柄 (BeginUpdateResource返回的句柄)。 它接收到资源类型的名称和资源名称,你可以用MAKEINTRESOURCE。(例如,类型是RC_ICON而且名称是MAKEINTRESOURCE(1),这将插入一个ID为1的图标资源。) 它还需要接收语言标识符。你需要用MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT)把资源的语言设为默认值。最后,UpdateResource接收到一个包含资源的缓存指针和这个缓存的长度。
你要用到的最后一个方法是EndUpdateResource。他接收到BeginUpdateResource提供的的句柄和一个布尔参数,如果这个布尔参数被设为真,将将丢弃所有对资源所做的更改。
源于Windows SDK文档:
HANDLE BeginUpdateResource(
LPCTSTR pFileName, // pointer to file in which to update
// resources
BOOL bDeleteExistingResources // deletion option
);
BOOL UpdateResource(
HANDLE hUpdate, // update-file handle
LPCTSTR lpType, // address of resource type to update
LPCTSTR lpName, // address of resource name to update
WORD wLanguage, // language identifier of resource
LPVOID lpData, // address of resource data
DWORD cbData // length of resource data, in bytes
);
BOOL EndUpdateResource(
HANDLE hUpdate, // update-file handle
BOOL fDiscard // write flag
);
放到一起
你的InjectMainIcon方法是什么样子的?
void InjectMainIcon(char *Where, char *What)
{
HANDLE hWhere = BeginUpdateResource(Where, FALSE);
char *buffer; // buffer to store raw icon data
long buffersize; // length of buffer
int hFile; // file handle
hFile = open(What, O_RDONLY | O_BINARY);
if (hFile == -1)
return; // if file doesn't exist, can't be opened etc.
// calculate buffer length and load file into buffer
buffersize = filelength(hFile);
buffer = (char *)malloc(buffersize);
read(hFile, buffer, buffersize);
close(hFile);
UpdateResource(
hWhere, // Handle to executable
RT_ICON, // Resource type - icon
MAKEINTRESOURCE(1), // Make the id 1
MAKELANGID(LANG_ENGLISH, // Default language
SUBLANG_DEFAULT),
(buffer+22),
// skip the first 22 bytes because this is the icon header
// and directory entry (if the file contains multiple
// images, the directory entries will be larger than 22 bytes)
buffersize-22 // length of buffer
);
// Again, we use this structure for educational purposes.
// The icon header and directory entries can be read from the
// file.
GROUPICON grData;
// This is the header
grData.Reserved1 = 0; // reserved, must be 0
grData.ResourceType = 1; // type is 1 for icons
grData.ImageCount = 1; // number of icons in structure (1)
// This is the directory entry
grData.Width = 32; // icon width (32)
grData.Height = 32; // icon height (32)
grData.Colors = 0; // colors (256)
grData.Reserved2 = 0; // reserved, must be 0
grData.Planes = 2; // color planes
grData.BitsPerPixel = 32; // bit depth
grData.ImageSize = buffersize - 22; // size of image
grData.ResourceID = 1; // resource ID is 1
UpdateResource(
hWhere,
RT_GROUP_ICON,
// RT_GROUP_ICON resources contain information about
// stored icons
"MAINICON",
// MAINICON contains information about the application's
// displayed icon
MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT),
&grData,
// Pointer to this structure
sizeof(GROUPICON)
);
free(buffer); // free memory
// Perform the update, don't discard changes
EndUpdateResource(hWhere, FALSE);
}
总结
系统显示的16x16的图标能够从32x32的图标资源中自动计算出,所以你没必要添加小图标。然而,可以修改这里的代码使它能够在运行时通过读取和分割图标文件的头部和目录项来插入所有的图标文件。