一、概述
想要获取一个可执行文件(PE文件)里包含的资源文件,首先要解析可执行文件,得到资源存储的地址及大小,可参考 https://blog.csdn.net/zhyulo/article/details/85717711 。然后,根据资源存储方式,得到各资源的数据内容及其大小,可参考 https://blog.csdn.net/zhyulo/article/details/85930045 。
PE文件的资源中,快捷键的资源类型ID=9。在RC文件中,快捷键的定义方式如下:
IDR_ACCELERATOR ACCELERATORS DISCARDABLE
BEGIN
"O", ID_FILE_OPEN, VIRTKEY, CONTROL, NOINVERT
"S", ID_FILE_SAVE, VIRTKEY, CONTROL, NOINVERT
END
二、快捷键的资源结构
快捷键在资源中,以AccelTableEntry快捷键结构数组方式存储,该结构体定义如下:
struct AccelTableEntry
{
WORD fFlags;//功能键
WORD wAnsi;//虚拟键值
WORD wId;//快捷键id
WORD padding;
};
fFlags: 功能键,比如Shift、Ctrl、Alt等按键值,该成员每一位都代表一个涵义,可通过或运算组合使用。如果第8位(0x80)值为1,代表该数组成员为最后一个元素。常见功能键值有:
#define FALT 16
#define FCONTROL 8
#define FNOINVERT 2
#define FSHIFT 4
#define FVIRTKEY 1
wAnsi: 按键值,比如F1~F24按键,字母按键,数字按键等等。按键值定义位于winuser.h文件中,以VK_开头定义。
wId: 快捷键响应的ID值。
padding: 4字节对齐用,没有实际意义。
三、示例程序
以下程序展示了如何从PE文件的快捷键资源流中读取并输出成RC文件形式的代码。
void GetAccel(void *buf, WORD id)
{
struct AccelTableEntry
{
WORD fFlags;//功能键
WORD wAnsi;//虚拟键值
WORD wId;//快捷键id
WORD padding;
} *Entry = (AccelTableEntry*)buf;
PutIDName(id, "IDR_ACCELERATOR");
Puts(" ACCELERATORS PRELOAD MOVEABLE PURE\nBEGIN\n");
for(int i=0; ; i++)
{
Puts("\t");
if(Entry[i].wAnsi >= 'A' && Entry[i].wAnsi <= 'Z')
Print("\"%c\",", Entry[i].wAnsi);
else if(Entry[i].wAnsi >= '0' && Entry[i].wAnsi <= '9')
Print("\"%c\",", Entry[i].wAnsi);
else if(Entry[i].wAnsi >= VK_F1 && Entry[i].wAnsi <= VK_F24)
Print("VK_F%d,", Entry[i].wAnsi - VK_F1 + 1);
else if(Entry[i].wAnsi >= VK_NUMPAD0 && Entry[i].wAnsi <= VK_NUMPAD9)
Print("VK_NUMPAD%d,", Entry[i].wAnsi - VK_NUMPAD0);
else Print("%d,", Entry[i].wAnsi);
Puts("\t");
PutIDName(Entry[i].wId, "ID_ACCEL");
if(Entry[i].fFlags & FVIRTKEY) Puts(", VIRTKEY");
else Puts(", ASCII");
if(Entry[i].fFlags & FNOINVERT) Puts(", NOINVERT");
if(Entry[i].fFlags & FSHIFT) Puts(", SHIFT");
if(Entry[i].fFlags & FCONTROL) Puts(", CONTROL");
if(Entry[i].fFlags & FALT) Puts(", ALT");
Puts("\n");
if(Entry[i].fFlags & 0x80) break;
}
Puts("END\n");
}