Windows Programming/资源脚本参考

维基教科书,自由的教学读本

本篇附录介绍资源脚本文件(Resource script file)。[1]

一般结构[编辑]

资源脚本文件是人可读的文本文件,用ANSI或UTF-16小端序带字节掩码“BOM”格式。

为支持多语种国际化,对ASNI格式,使用#pragma切换代码页。对Unicode格式使用#pragma 切换,而LANGUAGE仅支持Win32。

典型的小文件例子:

#include <windows.h>
#define IDC_STATIC -1

100 ICON "ProgIcon.ico"

10 MENU
{		// or BEGIN
 POPUP "&File"
 {
  MENUITEM "&Exit",IDCANCEL
 }
}		// or END

使用花括号或BEGIN/END都行。Visual Studio资源编辑器总是产生BEGIN/END对。

各种资源的格式为:

id_of_resource  resource_type  [memory management flags]  "filename"

id_of_resource  resource_type  [memory management flags]
BEGIN
 subsequent data
END

上述规则的例外情形:

  • LANGUAGE语句可以放在任何位置(仅限Win32)
  • DIALOGVERSIONINFO资源,在头行与BEGIN之间还有别的语句。
  • STRINGTABLE资源在关键字之前不需要资源ID,但每个资源需要ID前缀

id_of_resourceresource_type可以是字符串或数。这里不需要引号。数是更好的辨识资源的方法。所有预定的资源的类型都是数。

支持条件预编译指令 #if / #ifdef / #endif

用于ID的表达式限于非常简单的数学,不允许布尔运算符。

内部机制[编辑]

资源被编译为三级目录结构:

  1. 资源类型 (MENU, DIALOG 等)
  2. 资源ID
  3. 资源所用的语言

读二进制资源的API函数使用顺序示例:

FindResource()		// get a handle
LoadResource()		// get the binary size
LockResource()		// get a pointer; Win32: This is a simple macro 
…			// do something
UnlockResource()	// Win32: This is a do-nothing macro 
FreeResource()		// release

由于bitmap, icon, cursor, dialog, string table, menu资源没有官方文档,分析时有点费解,编程者应该使用专门的资源类型的加载函数。详见下述。

标识符[编辑]

建议以ID为开头,第三个字母表示资源类型:

  • IDS: A string resource
  • IDM: A menu resource
  • IDC: A command identifier
  • IDD: A dialog box resource
  • IDA: An Accelerator table resource
  • IDI: An Icon or bitmap resource
  • IDB: A Bitmap resource
  • ID: A custom resource, or an uncommon resource type.

有时,菜单中的命令的标识符使用前缀"IDM_",以区分其它资源的命令。

ID允许范围为0..65535,建议范围1..32767

LANGUAGE[编辑]

关键词LANGUAGE有不同的作用域:

  • 本地(用于一个资源)如果在资源行之下,例如:
21 MENU
LANGUAGE 7,1 // or, LANG_GERMAN, SUBLANG_GERMAN
{
 POPUP "&Datei"  // = "&File"
 …
  • 全局(适用于所有之下的资源)如果出现在别处:

语言中立资源,如无文化局限的icons, VersionInfo, Manifests 应当设置LANGUAGE 0,0 (或更繁琐一点 LANG_NETUTRAL,SUBLANG_NEUTRAL)

在一份RC文件中,同一个资源ID在不同语言下可出现多次。

内存管理标志[编辑]

Win16时代留下了一些内存管理标志,如MOVEABLE, FIXED等。详见LocalAlloc()

DISCARDABLE[编辑]

关键字DISCARDABLE在32位Windows上被忽略。用于向后兼容。[1]

Icons[编辑]

操作系统使用icon在用户界面种表示对象如文件、文件夹、快捷、应用程序、文档等。 操作系统提供了一套标准的icon,在SDK头文件种以IDI_为前缀定义了其标识符。

Icons在资源文件中用ICON关键字声明。例如:

IDI_ICON<n> ICON [DISCARDABLE] "iconfile.ico"

Windows Explorer使用资源脚本中第一个icon显示二进制可执行文件。例如,如果有2各icon:

IDI_ICON1 ICON DISCARDABLE "icon1.ico"
IDI_ICON2 ICON DISCARDABLE "icon2.ico"

在对应的resource.h中定义宏:

#define IDI_ICON1 1
#define IDI_ICON2 2

那么可执行文件以icon1.ico作为icon。

为可执行模块加载一个icon,假定我们有该实例的句柄(下例hInst),可以得到icon的句柄:

HICON hIcon;
hIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICON1));

Icon辨识符一般前缀为"IDI_"表示"ID for an Icon"。

LoadIcon()函数的第二个参数是一个字符串指针。如果该指针的高16位是零,Windows把它当作一个资源数的值,而不是一个字符串。Microsoft提供了宏MAKEINTRESOURCE把16位无符号数转为字符串指针。当然也可以用字符串来定义一个Icon:

MYICON1 ICON DISCARDABLE "icon1.ico"

可以用名字加载icon:

HICON hIcon;
hIcon = LoadIcon(hInst, "MYICON1");

资源的字符串标识符是大小写敏感的。

WNDCLASSEX有2个句柄值用于表示2个icon:大icon与小icon。小icon用于应用程序左上角,一般是16个像素正方形。大icon是32个像素正方形。如果不提供小icon,自动从大icon产生小icon。

LoadIcon()函数如果使用NULL实例句柄,则Windows提供缺省icon。

Win32 API已经允许用LoadImage函数加载icon、bitmap、鼠标光标。

二进制可执行文件内部用数值资源类型RT_ICON == 3存储只有单一图片的icon,用RT_GROUP_ICON == 14存储有一组图片的icon。

Windows使用4种icon尺寸:system small、system large、shell small、shell large。

  1. system small icon:在窗口标题条上显示。 int cx = GetSystemMetrics(SM_CXSMICON ); int cy = GetSystemMetrics(SM_CYSMICON ); 可获取其值。一般是16X16。
  2. system large icon:主要用于应用程序,也在Alt+Tab对话框显示。其尺寸不可更改。 int cx = GetSystemMetrics(SM_CXICON ); int cy = GetSystemMetrics(SM_CYICON ); 可获取其值。一般是32X32。
  3. shell small icon:用于Windows Explorer与common dialog。默认为system small的尺寸。用 SHGetFileInfo函数与ImageList_GetIconSize函数查询其尺寸。
  4. shell large icon:用于桌面。用 SHGetFileInfo函数与ImageList_GetIconSize函数查询其尺寸。

开始菜单使用shell small icons或shell large icons,取决于是否“Use large icons check box”被选中。

应用程序应该在资源中提供下述尺寸的icon:

  • 48x48, 256 color
  • 32x32, 16 color
  • 16x16 pixels, 16 color

位图[编辑]

资源文件中,位图定义为:

(bitmap ID or name) BITMAP [DISCARDABLE] "bitmapfile.bmp"

加载位图使用LoadBitmap函数(也可以用LoadImage函数):

HBITMAP hBmp;
hBmp = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BITMAP1));

或者加载用字符串来命名的位图资源:

hBmp = LoadBitmap(hInst, "MyBitmapRes");

位图是大型资源,如果加载到内存失败或者ID或名字值无效,该函数返回值为NULL。因此,使用前应该检查其句柄是否为空。

卸载位图用函数DeleteObject函数。

位图标识符一般以"IDB_"为前缀。

可执行模块内部,位图的数值资源类型为RT_BITMAP == 2。

鼠标光标[编辑]

加载鼠标光标使用LoadCursor函数。

可执行模块内部,鼠标光标的资源数值类型,当为单独图像为 RT_CURSOR == 1,一组图像时为 RT_GROUP_CURSOR == 12。

除了明确指出外部的二进制资源的文件,资源编译器允许资源文件内联的二进制数据。例如:

42 ICON
{
 123,4567,0x89AB,0xCDEF
 '\x01','\x23',"ajx"
}

源自Win16的遗产:

  • 十进制或十六进制的数作为未对齐的16位小尾值。
  • 字符作为8位值存储。

字符串表[编辑]

一个资源脚本文件中可以有多个字符串表。编译时会自动合并为一个。例子:

STRINGTABLE DISCARDABLE
BEGIN
   IDS_STRING1, "This is my first string"
   IDS_STRING2, "This is my second string"
   ...
END

使用LoadString函数装入字符串:

int LoadString(HINSTANCE hInstance, UINT uID, LPTSTR lpBuffer, int nBufferMax);

注意该函数可能会返回丰富的警告信息。msdn Windows会在返回的字符串自动以0字节结尾。

加速键[编辑]

菜单[编辑]

版本信息[编辑]

StringFileInfo预定义名称
名称 描述
CompanyName 生成文件的公司,例如 "Microsoft Corporation" 或 "Standard Microsystems Corporation,Inc." 此字符串是必需的。
FileDescription 要向用户显示的文件说明。 当用户选择要安装的文件时,此字符串可能会显示在列表框中,例如 "AT-Style 键盘的键盘驱动程序"。 此字符串是必需的。
FileVersion 文件的版本号,例如 "3.10" 或 "5.00. RC2"。 此字符串是必需的。
InternalName 文件的内部名称(如果存在)(例如,如果文件为动态链接库,则为模块名称)。 如果该文件没有内部名称,则此字符串应为原始文件名,而不包含扩展名。 此字符串是必需的。
LegalCopyright 适用于该文件的版权声明。 这应包括所有声明的完整文本、合法符号、版权日期等。 此字符串是可选的。
LegalTrademarks 适用于该文件的商标和注册商标。 这应包括所有声明的完整文本、合法符号、商标号等。 此字符串是可选的。
OriginalFilename 文件的原始名称,不包括路径。 此信息使应用程序能够确定文件是否已被用户重命名。 名称的格式取决于为其创建该文件的文件系统。 此字符串是必需的。
PrivateBuild 有关文件私有版本的信息(例如,"由 TESTER1 在 TESTBED 上生成 \ ")。 仅当在根块的 fileflags 参数中指定 VS _ FF _ PRIVATEBUILD 时,才应提供此字符串。
ProductName 用于分发文件的产品的名称。 此字符串是必需的。
ProductVersion 用于分发该文件的产品的版本,例如 "3.10" 或 "5.00. RC2"。 此字符串是必需的。
SpecialBuild 指示此版本的文件与标准版本的不同之处的文本,例如 "用于 TESTER1 的专用生成解决 M250 和 M250E 计算机上的鼠标问题"。 仅当在根块的 fileflags 参数中指定 VS _ FF _ SPECIALBUILD 时,才应提供此字符串。
Comments 出于诊断目的应显示的其他信息

例如:

BLOCK "StringFileInfo"
  BEGIN
      BLOCK "040904E4"
      BEGIN
          VALUE "CompanyName",      "My Company.\0"
          VALUE "FileDescription",  "A Win32 program."
          VALUE "FileVersion",      "1.0.0.0\0"
          VALUE "ProductName",      "The product name.\0"
          VALUE "ProductVersion",   "1.0\0"
          VALUE "LegalCopyright",   "My Company.\0"
      END
  END
  BLOCK "VarFileInfo"
  BEGIN 
      /* 只能有一行,但可以包含一对或多对WORD */
      VALUE "Translation", 0x409, 1252
  END

对话框[编辑]

对话框资源的通用模式:

(Dialog ID or name) DIALOG [DISCARDABLE] x, y, width, height
TITLE "(dialog box title)"
[CLASS "(class name)"]
FONT "(font name)"
BEGIN
   ...
END

如果一个对话框没有关联一个CLASS,那么CLASS域不需要填写。所有字符串必须用双引号包含。

一般控件[编辑]

CONTROL classname,windowname,id,left,top,width,height,windowflags

特殊按钮[编辑]

编辑框[编辑]

Manifests[编辑]

Manifest资源包含UTF-8编码的XML描述信息,关于操作系统与DLL依赖。

FONT[编辑]

FONTDIR[编辑]

RCDATA[编辑]

MESSAGETABLE[编辑]

定义应用程序的消息表资源的ID与文件。消息表是特殊的字符串资源用于event logging以及FormatMessage函数。文件中包含消息编译器MC.EXE产生的二进制消息表。[2]

输入给消息编译器MC.EXE的message text (.mc) 文件采用语法见[3],例子:

; // ***** Sample.mc *****
; // This is the header section.

MessageIdTypedef=DWORD

SeverityNames=(Success=0x0:STATUS_SEVERITY_SUCCESS
Informational=0x1:STATUS_SEVERITY_INFORMATIONAL
Warning=0x2:STATUS_SEVERITY_WARNING
Error=0x3:STATUS_SEVERITY_ERROR
)

FacilityNames=(System=0x0:FACILITY_SYSTEM
Runtime=0x2:FACILITY_RUNTIME
Stubs=0x3:FACILITY_STUBS
Io=0x4:FACILITY_IO_ERROR_CODE
)

LanguageNames=(English=0x409:MSG00409)
LanguageNames=(Japanese=0x411:MSG00411)

; // The following are message definitions.

MessageId=0x1
Severity=Error
Facility=Runtime
SymbolicName=MSG_BAD_COMMAND
Language=English
You have chosen an incorrect command.
.

Language=Japanese
<Japanese message string goes here>
.

MessageId=0x2
Severity=Warning
Facility=Io
SymbolicName=MSG_BAD_PARM1
Language=English
Cannot reconnect to the server.
.

Language=Japanese
<Japanese message string goes here>
.

MessageId=0x3
Severity=Success
Facility=System
SymbolicName=MSG_STRIKE_ANY_KEY
Language=English
Press any key to continue . . . %0
.

Language=Japanese
<Japanese message string goes here>
.

MessageId=0x4
Severity=Error
Facility=System
SymbolicName=MSG_CMD_DELETE
Language=English
File %1 contains %2 which is in error.
.

Language=Japanese
<Japanese message string goes here>
.

MessageId=0x5
Severity=Informational
Facility=System
SymbolicName=MSG_RETRYS
Language=English
There have been %1!d! attempts with %2!d!%% success%! Disconnect from 
the server and try again later.
.

Language=Japanese
<Japanese message string goes here>
.

TEXTINCLUDE[编辑]

TEXTINCLUDE是一种资源类型。目的是安全地存储Set Include information,使Visual C++的Set Includes对话框可以表达它们。[4]

Visual C++识别3种特定的TEXTINCLUDE资源,其起源标识数分别是1, 2, 3:

Visual C++识别3种特定的TEXTINCLUDE资源
TEXTINCLUDE resource ID Type of Set Includes information
1 Symbol Header File
2 Read-Only Symbol Directives
3 Compile-Time Directives

用户定义资源[编辑]

用户定义资源应当使用更大的资源类型标识符,如RT_RCDATA == 10.

DLGINCLUDE[编辑]

包含菜单、对话框的#define语句的头文件。资源编辑器使用。

多语言的资源[编辑]

在单个exe文件中可嵌入多种语言的资源。但Visual Studio资源编辑器不支持,所以必须手工编辑。

使用.rc2文件定义资源,因为Visual Studio资源编辑器不会修改它。需要把.rc2存为UTF-16 LE编码并且以空行结束。

一个MFC项目创建时就有了一个空的.rc2文件。我们称它为"main" .rc2文件。在main .rc2文件中,对每种语言增加一行#include该语言的.rc2文件:

   #include "lang_en.rc2"
   #include "lang_de.rc2"
   // Restore default language for resources included after current file
   LANGUAGE LANG_ENGLISH, SUBLANG_NEUTRAL

创建语言相关的.rc2文件。每个文件以该语言的声明 LANGUAGE <LANGID>, <SUBLANGID> 开始,如lang_en.rc2文件中:

   LANGUAGE LANG_ENGLISH, SUBLANG_NEUTRAL
   STRINGTABLE
   BEGIN
       IDS_STRING1 "Stack Overflow"
       IDS_STRING2 "Stack Overflow is a privately held website, the flagship site of the Stack Exchange Network, created in 2008 by Jeff Atwood and Joel Spolsky."
   END

在文件lang_de.rc2中:

   LANGUAGE LANG_GERMAN, SUBLANG_NEUTRAL
   STRINGTABLE
   BEGIN
       IDS_STRING1 "Stapelüberlauf"
       IDS_STRING2 "Stack Overflow (englisch für Stapelüberlauf) ist eine Internetplattform, auf der angemeldete Benutzer Fragen zum Thema Softwareentwicklung stellen können."
   END

编译可执行文件并在资源编辑器中检查已经包括了多种语言。也可以在Visual Studio打开.exe程序,查看它的资源。

在源代码中,可以正常加载资源,Windows自动根据当前用户locale加载对应语言的资源。如果没有匹配的资源,它会加载英语资源。

也可以用FindResourceEx函数加载明确给出的某种语言的资源,包括标准MFC资源 afxres.h 。

标准MFC资源 如afxres,可以包含在特点语言的.rc2文件中,如:

LANGUAGE LANG_GERMAN, SUBLANG_NEUTRAL
#ifdef __AFXRES_RC__
   #undef __AFXRES_RC__    // To be able to include multiple language versions of afxres.rc
#endif
#include "l.deu\afxres.rc"  // Standard MFC resources
STRINGTABLE
BEGIN
   IDS_STRING1 "Stapelüberlauf"
   IDS_STRING2 "Stack Overflow (englisch für Stapelüberlauf) ist eine Internetplattform, auf der angemeldete Benutzer Fragen zum Thema Softwareentwicklung stellen können."
END

参考文献[编辑]

  1. MSDN:Working with Resource Files
  2. MSDN:Message Compiler (MC.exe)
  3. MSDN:Message Text Files
  4. TN035: Using Multiple Resource Files and Header Files with Visual C++