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)DIALOG
与VERSIONINFO
资源,在头行与BEGIN之间还有别的语句。STRINGTABLE
资源在关键字之前不需要资源ID,但每个资源需要ID前缀
id_of_resource
与resource_type
可以是字符串或数。这里不需要引号。数是更好的辨识资源的方法。所有预定的资源的类型都是数。
支持条件预编译指令 #if / #ifdef / #endif
用于ID的表达式限于非常简单的数学,不允许布尔运算符。
内部机制[编辑]
资源被编译为三级目录结构:
- 资源类型 (MENU, DIALOG 等)
- 资源ID
- 资源所用的语言
读二进制资源的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)
内存管理标志[编辑]
Win16时代留下了一些内存管理标志,如MOVEABLE, FIXED等。详见LocalAlloc()
DISCARDABLE[编辑]
关键字DISCARDABLE在32位Windows上被忽略。用于向后兼容。[1] 32 bit resources are never loaded but mapped into memory.
Icons[编辑]
Template:TranH
Icons can be stored in a resource file using the ICON
keyword. Here is a general example of using an icon in a resource script:
IDI_ICON<n> ICON [DISCARDABLE] "iconfile.ico"
Windows Explorer will display the binary executable with the first icon from the script. For instance, if we load two icons, as such:
IDI_ICON1 ICON DISCARDABLE "icon1.ico" IDI_ICON2 ICON DISCARDABLE "icon2.ico"
And we define our macros as such in the corresponding resource.h
:
#define IDI_ICON1 1 #define IDI_ICON2 2
The executable file will have icon1.ico as its icon.
To load an icon from an executable module, assuming we have an instance handle to the module (hInst
in the following example), we can get a handle to the icon as such:
HICON hIcon; hIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICON1));
This will return a handle to the icon associated with the identifier "IDI_ICON1". Icon identifiers are generally prefixed with an "IDI_" which is short for "ID for an Icon".
The second parameter to the LoadIcon()
function is a pointer to a string. String pointers are 32 bit values. However, if the most significant 16 bits are all zero, Windows will treat the value as a resource number, and not a string. To make the conversion between a string and a 16-bit integer, Microsoft provides the MAKEINTRESOURCE macro. Similarly, we could have used a string to define our Icon:
MYICON1 ICON DISCARDABLE "icon1.ico"
And we could load this string by name:
HICON hIcon; hIcon = LoadIcon(hInst, "MYICON1");
String identifiers for resources are case insensitive.
WNDCLASSEX
has handle values for two icons: a large icon and a small icon. The small icon is the icon used in the upper-left corner. Small icons are generally 16 pixels square. Larger icons are 32 pixels square. If no small icon handle is provided, the large icon will be shrunk down to fit.
If the LoadIcon()
function is supplied with a NULL instance handle, Windows will supply a default icon for use.
Recently, the Win32 API provides the LoadImage function for loading icons, bitmaps, and mouse cursors from a single function. You can find more information about this function on MSDN.
Internally, Icons are stored under numeric resource type RT_ICON == 3, and are grouped under RT_GROUP_ICON == 14. Template:TranF
位图[编辑]
Bitmaps can be loaded similarly to Icons in resource files:
(bitmap ID or name) BITMAP [DISCARDABLE] "bitmapfile.bmp"
Bitmaps can be accessed with the aptly named LoadBitmap function (again, new versions of the Win32 API prefer you use LoadImage to load a bitmap, icon, or cursor). LoadBitmap returns an HBITMAP handle type:
HBITMAP hBmp; hBmp = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BITMAP1));
Or, if we have named our bitmap resource:
hBmp = LoadBitmap(hInst, "MyBitmapRes");
Bitmaps are large resources, and if windows can't load the bitmap into memory (or if the ID or name value is invalid), the function will return a NULL value. Make sure you test this value before you use the handle.
Bitmaps must be unloaded from memory by passing the handle to the DeleteObject() function. You can find more information about this on MSDN
Bitmap identifiers generally use a "IDB_" prefix, to indicate that it is the ID of a bitmap.
Internally, Bitmaps are stored under numeric resource type RT_BITMAP == 2.
鼠标光标[编辑]
Mouse cursors are specified similarly to icons and bitmaps, and are loaded with the LoadCursor function.
Internally, cursors are stored under numeric resource type RT_CURSOR == 1, and are grouped under RT_GROUP_CURSOR == 12.
As for any resource that implies binary data, the use of an external file with "filename" is recommended. However, most resource compilers allow to inline binary data to the resource file in this way:
42 ICON { 123,4567,0x89AB,0xCDEF '\x01','\x23',"ajx" }
with this rule, coming from Win16 heritage:
- Numbers (decimal or hexadecimal) are stored contiguously as 16-bit little-endian quantities (unaligned)
- Characters are stored as 8-bit quantities
字符串表[编辑]
一个资源脚本文件中可以有多个字符串表。编译时会自动合并为一个。例子:
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字节结尾。
加速键[编辑]
Keyboard accelerators are a common part of nearly every windows application, and therefore it is a good idea to simplify the job of creating accelerators by putting them in a resource script. Here is how to create an accelerator table:
(Accelerator Table ID or name) ACCELERATORS [DISCARDABLE] BEGIN (key combination), (Command ID) ... END
Key combinations are specified in terms of either a string literal character ("A" for instance) or a virtual key code value. Here are some examples:
IDA_ACCEL_TABLE ACCELERATORS DISCARDABLE BEGIN "A", IDA_ACTION_A //Shift+A END
Now, when the key combination "Shift+A" is pressed, your window procedure will receive a WM_COMMAND message with the value IDA_ACTION_A in the WPARAM field of the message.
If we want to use combinations of the "Alt" key, or the "Ctrl" key, we can use the ALT and CONTROL keywords, respectively:
IDA_ACCEL_TABLE ACCELERATORS DISCARDABLE BEGIN "a", IDA_ACTION_A, ALT //Alt+A "b", IDA_ACTION_B, CONTROL //Ctrl+B "c", IDA_ACTION_C, ALT, CONTROL //Alt+Ctrl+A END
Also, we can use the "^" symbol to denote a CONTROL key code:
IDA_ACCEL_TABLE ACCELERATORS DISCARDABLE BEGIN "^a", IDA_ACTION_A //Control+A END
Similarly, if we want to be super hackers, would could use the ASCII code directly:
IDA_ACCEL_TABLE ACCELERATORS DISCARDABLE BEGIN 65, IDA_ACTION_A, ASCII //65 = "A", Shift+A END
Or, we could refer to keys (including non-alphanumeric keys) with their Virtual Key Code identifiers, by using the VIRTKEY identifier:
IDA_ACCEL_TABLE ACCELERATORS DISCARDABLE BEGIN VK_F12, IDA_ACTION_F12, VIRTKEY //press the "F12 Key" VK_DELETE, IDA_ACTION_DEL, VIRTKEY, CONTROL //Ctrl+Delete END
Now, If we make an accelerator correspond to a menu command, the menu command will light up when we press the accelerator. That is, the menu will light up unless we specify the "NOINVERT" keyword:
IDA_ACCEL_TABLE ACCELERATORS DISCARDABLE BEGIN "A", IDA_ACTION_A, NOINVERT //Shift+A (non inverted menu selection) END
To Load an accelerator table, we need to use the LoadAccelerators function, as such:
HACCEL hAccel; hAccel = LoadAccelerators(hInst, MAKEINTRESOURCE(IDA_ACCEL_TABLE));
Again, we could have given our resource a string name, and used that string to load the table.
When using accelerators, we need to alter our message loop to intercept the keypress messages, and translate them into command messages according to our accelerator table rules. We use the TranslateAccelerator function, to intercept the keypress messages, and translate them into command messages, as such:
while ( (Result = GetMessage(&msg, NULL, 0, 0)) != 0) { if (Result == -1) { // error handling } else { if (!TranslateAccelerator(hwnd, haccel, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } }
Also, if we are writing an MDI application, we need to intercept Accelerator messages from the child windows, we use the TranslateMDISysAccel function also:
while ( (Result = GetMessage(&msg, NULL, 0, 0)) != 0) { if (Result == -1) { // error handling } else { if ( !TranslateMDISysAccel(hwndClient, &msg) && !TranslateAccelerator(hwndFrame, haccel, &msg) ) { TranslateMessage(&msg); DispatchMessage(&msg); } } }
Where "hwndFrame" is the handle to the frame window, and "hwndClient" is the handle to the MDI client window.
Internally, Accelerators are stored under numeric resource type RT_ACCELERATOR == 9.
菜单[编辑]
Menus can be defined in a resource script using the MENU keyword. There are 2 types of items that appear in a menu, the top level "POPUP" menu items, and the secondary "MENUITEM" items. These are defined in a menu as such:
(ID or name) MENU [DISCARDABLE] BEGIN POPUP "File" POPUP "Edit" BEGIN MENUITEM "Copy", IDM_EDIT_COPY MENUITEM "Paste", IDM_EDIT_PASTE END ... END
We have included a few examples here, so that you can see the difference between a POPUP and a MENUITEM. When we have a menu with the ID_MENU identifier, we can load it into our program as such:
HMENU hmenu; hmenu = LoadMenu(hInst, MAKEINTRESOURCE(ID_MENU));
Once we have this handle, we can pass it to the CreateWindow function, and apply it to our window.
When a menu item is selected, the host program receives a WM_COMMAND message, with the menu item identifier in the WPARAM parameter. If we have a basic window procedure switch-case statement, we can see this as follows:
case WM_COMMAND: switch(WPARAM) { case IDM_EDIT_COPY: //handle this action break; case IDM_EDIT_PASTE: //handle this action break; } break;
In a menu, if we want to associate a menu item with an accelerator, we can define it as such:
ID_MENU MENU DISCARDABLE BEGIN POPUP "File" POPUP "Edit" BEGIN MENUITEM "&Copy", IDM_EDIT_COPY MENUITEM "&Paste", IDM_EDIT_PASTE END ... END
Notice how we put the ampersand (&) in front of the "C" in "Copy" and the "P" in "Paste". This means that those letters will be underlined, but more importantly, if an accelerator key combination is pressed, those items in the menu will be highlighted (unless the NOINVERT tag is specified in the accelerator table). If an ampersand is placed before a POPUP menu item, pressing ALT+ that letter will popup that menu. For instance, lets define our menu:
ID_MENU MENU DISCARDABLE BEGIN POPUP "&File" POPUP "&Edit" BEGIN MENUITEM "Copy", IDM_EDIT_COPY MENUITEM "Paste", IDM_EDIT_PASTE END ... END
Now, if we press ALT+F, we will pop open the File menu, and if we press ALT+E it will open the Edit menu. That's pretty nice functionality for only a single extra character to type.
Internally, Menus are stored under numeric resource type RT_MENU == 4.
版本信息[编辑]
例如:
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 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>
.
用户定义资源[编辑]
用户定义资源应当使用更大的资源类型标识符,如RT_RCDATA == 10.
DLGINCLUDE[编辑]
包含菜单、对话框的#define语句的头文件。资源编辑器使用。