《Windows游戏编程大师技巧》学习笔记(四)

资源与Windows应用程序

Windows程序支持将资源数据编译到.EXE文件中,优势如下:

  • 更容易发布
  • 不易丢失附加资源
  • 无法被轻易访问和修改

资源与Windows应用程序关系

支持编译到程序中的资源类型有:图标,光标,字符串,声音,位图,对话框,图元文件
我们只需要创建扩展名为.RC的文本文件,资源编译器会自动根据所配置的资源脚本装载所需的资源,并编译到以.RES为扩展名的文件中,后续与.LIB.OBJ文件编译为一个完整的应用程序文件;

编译及链接时的资源数据流程

使用图标资源

.RC脚本中使用如下方式定义ICON资源:

1
2
3
4
// 使用字符串命名
icon_name ICON FILENAME.ICO
// 使用整数ID命名
icon_id ICON FILENAME.ICO

资源脚本中支持引入头文件,所以我们就可以在头文件中给整数ID定义含义明确的名字,如下:

1
2
3
// 在头文件 RESOURCES.H 中
#define LARGE_ICON 100
#define SMALL_ICON 101
1
2
3
4
// 在资源脚本 xxx.RC 中
#include "RESOURCES.H"
LARGE_ICON ICON large.ico
SMALL_ICON ICON small.ico

使用字符串命名资源和使用整数ID命名资源本质相同,只是在加载使用时写法不一样:

1
2
3
4
5
// 加载使用字符串命名的图标资源
winclass.hIcon = LoadIcon(hinstance, "icon_name");

// 加载使用整数 ID 命名的图标资源
winclass.hIcon = LoadIcon(hinstance, MAKEINTRESOURCE(ICON_ID));

MAKEINTRESOURCE宏的作用是将整数转换为一个字符串指针,相关定义如下:

1
2
3
4
5
6
7
#define MAKEINTRESOURCEA(i) ((LPSTR)((ULONG_PTR)((WORD)(i))))
#define MAKEINTRESOURCEW(i) ((LPWSTR)((ULONG_PTR)((WORD)(i))))
#ifdef UNICODE
#define MAKEINTRESOURCE MAKEINTRESOURCEW
#else
#define MAKEINTRESOURCE MAKEINTRESOURCEA
#endif // !UNICODE

使用光标资源

资源的定义和加载与上述操作相似,不再赘述;
除去在窗口类初始化时定义其光标,还可以后续通过SetCursor函数改变当前窗口的光标,如下:

1
2
3
4
5
6
// 初始化窗口类时设置光标
winclass.hCursor = LoadCursor(hinstance, ICON_NAME_1);

// 后续过程中改变窗口光标
HCURSOR hNewCursor = LoadCursor(hinstance, ICON_NAME_2);
SetCursor(hNewCursor);

使用字符串资源

字符串使用如下方式的字符串表进行定义:

1
2
3
4
5
6
STRINGTABLE
{
ID_STRING_1, "string1"
ID_STRING_2, "string2"
...
}

花括号也可以被替换成BEGIN...END对;
需要注意的是,只能有一个字符串表,来包含所有的字符串资源,并且字符串在定义时不支持换行,每行长度不能超过255字符(包括字符串名在内);
除此之外,字符串资源只能使用者整数ID来定义,而不允许使用字符串标识来定义
字符串资源可以通过LoadString函数进行加载,在非Unicode环境下原型如下:

1
2
3
4
5
6
int WINAPI LoadStringA(
HINSTANCE hInstance, // 应用程序句柄
UINT uID, // 资源标识
LPSTR lpBuffer, // 字符串缓冲区地址
int cchBufferMax // 缓冲区大小
);

函数在调用成功时返回加载的字符数量,失败时返回0,示例如下:

1
2
3
4
5
6
7
8
char str_buffer[80];

if (!LoadString(hinstance, STRING_ID, str_buffer, 80))
{
// handle error
}

// use the string now

使用声音资源

标准的Windows声音资源只支持.WAV格式的文件
定义声音资源的方式与前述相同,并且声音资源和图标资源一样,支持使用字符串或整数ID定义;
静态装载声音资源较为复杂,此处只介绍使用PlaySound函数即时装载和播放声音资源的技巧;
在非Unicode环境下,PlaySound函数原型如下:

1
2
3
4
5
BOOL WINAPI PlaySoundA(
LPCSTR pszSound, // 文件名或资源标识
HMODULE hmod, // 应用程序句柄
DWORD fdwSound // 播放配置属性
);

关于参数DWORD fdwSound的取值,可以是下表中的一个或多个值:

fdwSound参数取值表一

fdwSound参数取值表二

1
PlaySound("SOUND_NAME", hinstance, SND_ASYNC | SND_LOOP | SND_RESOURCE);

需要注意的是,PlaySound函数位于头文件mmsystem.h中,而如果在包含Windows.h前定义了WIN32_LEAN_AND_MEAN这个宏,那么mmsystem.h就会被排除在外,所以可以单独引入该头文件或者取消上述宏定义;

总结

虽然手写资源脚本很酷,但是现代化的IDE都对自动生成资源配置提供了用户界面,知其所以然,而后在开发中选择高效的生产工具。

VisualStudio资源添加界面

作者

Voidmatrix

发布于

2022-01-28

更新于

2022-01-28

许可协议

评论