
RT_GROUP_ICON 是 Windows 可执行文件中用于存储图标资源的重要结构,它本质上是一个图标目录,用于管理多个不同尺寸和颜色深度的图标图像。
参考:Winuser.h) (资源类型 - Win32 apps | Microsoft Learn
基本概念
RT_GROUP_ICON 资源类型的 ID 为 14(0x0E),它与 RT_ICON 资源配合使用:
- RT_GROUP_ICON:作为图标目录,描述了一组图标的元数据
- RT_ICON:存储实际的图标图像数据
这种结构类似于 ICO 文件,但有重要区别:RT_GROUP_ICON 不直接存储图像数据,而是通过资源 ID 引用对应的 RT_ICON 资源。
RT_GROUP_ICON (ID=IDI_MAIN)
├─ GRPICONDIR
│ ├─ idReserved=0
│ ├─ idType=1
│ ├─ idCount=2 (2 个图标)
│ └─ idEntries[2]
│ ├─ 第 1 项:bWidth=16, bHeight=16, wBitCount=32, nID=101 (关联 RT_ICON 101)
│ └─ 第 2 项:bWidth=32, bHeight=32, wBitCount=32, nID=102 (关联 RT_ICON 102)
└─
RT_ICON (ID=101) → 16×16 32 位图标数据(ICONIMAGE 结构)
RT_ICON (ID=102) → 32×32 32 位图标数据(ICONIMAGE 结构)
数据结构定义
GRPICONDIR 结构(图标目录头)
typedef struct {
WORD idReserved; // 保留字段,必须为 0
WORD idType; // 资源类型,图标为 1
WORD idCount; // 图标数量
GRPICONDIRENTRY idEntries[1]; // 图标条目数组
} GRPICONDIR, *LPGRPICONDIR;
GRPICONDIRENTRY 结构(图标条目)
typedef struct {
BYTE bWidth; // 图标宽度(像素)
BYTE bHeight; // 图标高度(像素)
BYTE bColorCount; // 颜色数量(0 表示 >=8bpp)
BYTE bReserved; // 保留字段,必须为 0
WORD wPlanes; // 颜色平面数
WORD wBitCount; // 每像素位数
DWORD dwBytesInRes;// 资源数据大小(字节)
WORD nID; // 对应的 RT_ICON 资源 ID
} GRPICONDIRENTRY, *LPGRPICONDIRENTRY;
与 ICO 文件格式的区别
| 特征 | ICO 文件格式 | RT_GROUP_ICON 格式 |
|---|---|---|
| 最后一个字段 | dwImageOffset(文件偏移量) | nID(资源 ID) |
| 数据存储 | 直接包含图像数据 | 仅包含引用,图像数据在 RT_ICON 中 |
| 用途 | 磁盘文件存储 | EXE/DLL 资源存储 |
完整结构示例
假设一个包含 2 个图标的 RT_GROUP_ICON 资源:
[GRPICONDIR]
idReserved: 0x0000
idType: 0x0001
idCount: 0x0002
[GRPICONDIRENTRY 1]
bWidth: 32
bHeight: 32
bColorCount: 0
bReserved: 0
wPlanes: 1
wBitCount: 32
dwBytesInRes: 4096
nID: 1
[GRPICONDIRENTRY 2]
bWidth: 16
bHeight: 16
bColorCount: 0
bReserved: 0
wPlanes: 1
wBitCount: 32
dwBytesInRes: 1024
nID: 2
Delphi 中处理 RT_GROUP_ICON 的示例代码
读取 RT_GROUP_ICON 资源
uses
Windows, SysUtils;
type
TGRPICONDIR = record
idReserved: Word;
idType: Word;
idCount: Word;
idEntries: array[0..0] of TGRPICONDIRENTRY;
end;
PGRPICONDIR = ^TGRPICONDIR;
TGRPICONDIRENTRY = record
bWidth: Byte;
bHeight: Byte;
bColorCount: Byte;
bReserved: Byte;
wPlanes: Word;
wBitCount: Word;
dwBytesInRes: DWORD;
nID: Word;
end;
PGRPICONDIRENTRY = ^TGRPICONDIRENTRY;
procedure ReadGroupIcon(const ExeFileName: string);
var
hModule: THandle;
hResource: THandle;
pResource: PByte;
pGroupIcon: PGRPICONDIR;
pEntry: PGRPICONDIRENTRY;
i: Integer;
begin
hModule := LoadLibraryEx(PChar(ExeFileName), 0, LOAD_LIBRARY_AS_DATAFILE);
if hModule = 0 then
RaiseLastOSError;
try
hResource := FindResource(hModule, MAKEINTRESOURCE(1), RT_GROUP_ICON);
if hResource = 0 then
RaiseLastOSError;
pResource := LockResource(LoadResource(hModule, hResource));
if pResource = nil then
RaiseLastOSError;
pGroupIcon := PGRPICONDIR(pResource);
Writeln(Format('图标数量: %d', [pGroupIcon.idCount]));
Writeln('资源类型: ', pGroupIcon.idType);
pEntry := @pGroupIcon.idEntries[0];
for i := 0 to pGroupIcon.idCount - 1 do
begin
Writeln(Format('图标 %d:', [i + 1]));
Writeln(Format(' 尺寸: %dx%d', [pEntry.bWidth, pEntry.bHeight]));
Writeln(Format(' 颜色深度: %d bpp', [pEntry.wBitCount]));
Writeln(Format(' 数据大小: %d 字节', [pEntry.dwBytesInRes]));
Writeln(Format(' RT_ICON ID: %d', [pEntry.nID]));
Writeln;
Inc(pEntry);
end;
finally
FreeLibrary(hModule);
end;
end;
更新 EXE 文件的图标资源
procedure UpdateExeIcon(const IconFilename, ExeFilename: string);
var
hUpdate: THandle;
IconStream: TFileStream;
IconData: array of Byte;
IconSize: DWORD;
IconDir: TIconDir;
IconEntry: TIconDirEntry;
IconImage: TIconImage;
i: Integer;
begin
// 打开图标文件
IconStream := TFileStream.Create(IconFilename, fmOpenRead or fmShareDenyNone);
try
IconSize := IconStream.Size;
SetLength(IconData, IconSize);
IconStream.ReadBuffer(IconData[0], IconSize);
// 解析 ICO 文件头
Move(IconData[0], IconDir, SizeOf(TIconDir));
if (IconDir.idReserved <> 0) or (IconDir.idType <> 1) then
raise Exception.Create('无效的 ICO 文件');
// 开始更新 EXE 资源
hUpdate := BeginUpdateResource(PChar(ExeFilename), False);
if hUpdate = 0 then
RaiseLastOSError;
try
// 更新 RT_GROUP_ICON
if not UpdateResource(hUpdate, RT_GROUP_ICON, MAKEINTRESOURCE(1),
MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), @IconData[0], IconSize) then
RaiseLastOSError;
// 更新对应的 RT_ICON 资源
for i := 0 to IconDir.idCount - 1 do
begin
Move(IconData[SizeOf(TIconDir) + i * SizeOf(TIconDirEntry)],
IconEntry, SizeOf(TIconDirEntry));
// 读取图标图像数据
Move(IconData[IconEntry.dwImageOffset], IconImage, SizeOf(TIconImage));
// 更新 RT_ICON 资源
if not UpdateResource(hUpdate, RT_ICON, MAKEINTRESOURCE(i + 1),
MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
@IconData[IconEntry.dwImageOffset], IconEntry.dwBytesInRes) then
RaiseLastOSError;
end;
// 完成更新
if not EndUpdateResource(hUpdate, False) then
RaiseLastOSError;
except
EndUpdateResource(hUpdate, True);
raise;
end;
finally
IconStream.Free;
end;
end;
关键要点
- RT_GROUP_ICON 与 RT_ICON 的关系:前者是目录,后者是实际数据
- 资源 ID 的重要性:GRPICONDIRENTRY.nID 必须与对应的 RT_ICON 资源 ID 匹配
- 多语言支持:可以为不同语言创建不同的 RT_GROUP_ICON 资源
- 兼容性:从 Windows Vista 开始支持 PNG 压缩的图标数据
- 资源更新:更新图标时需要同时更新 RT_GROUP_ICON 和对应的 RT_ICON 资源
理解 RT_GROUP_ICON 格式对于处理 Windows 应用程序图标、资源编辑和 EXE 文件修改等任务非常重要。
15

被折叠的 条评论
为什么被折叠?



