Resource files represent one of the most prominent features of Windows programming. It is through resource files that most applications define the visible elements of their user interface: menus, dialogs, text strings, bitmaps, and other types of resources.
Resource files are created in a form readable by humans and compiled with the Resource Compiler. The compiled result is usually linked with the rest of the application to form a single binary image that contains executable code and resource information.
Strictly speaking, use of resource files is not mandatory. However, it would be foolish to write needlessly complex code to implement an application's user-interface elements when resource files provide a convenient way to do the same. Furthermore, the use of resource files makes it much easier to replace language-dependent user-interface elements when working on an application that will be used in multiple language environments.
In the "good old days" programmers used a text editor to edit a resource file. These days, hand-editing of resource files is rare. Instead, graphical resource editors are used, such as the integrated resource editor that is part of the Developer Studio. Nevertheless, the ability to make sense of resource file contents, the ability to hand-edit resource files when necessary, can still prove to be a useful skill. The syntax and the structure of resource files are simple, and understanding what is "under the hood" helps put the features of sophisticated editors in perspective.
This chapter presents a review of the use of resource files and the resource compiler.
A resource file, or resource script may contain any number of resource script statements and preprocessor directives. For example, consider the sample resource file shown in Listing 10.1.
#include <windows.h> DlgBox DIALOG 20, 20, 90, 64 STYLE DS_MODALFRAME | WS_CAPTION | WS_SYSMENU CAPTION "Sample Dialog" BEGIN DEFPUSHBUTTON "Sample Button", IDOK, 19, 44, 52, 14, WS_GROUP CTEXT "Sample Message", -1, 0, 8, 90, 8 END
This simple script defines a dialog with a button and a static text field (Figure 10.1). The dialog will be referred to by name (DlgBox) from the application using this resource. Although it is not obvious at first sight, some constants (for example, IDOK, DS_MODALFRAME or WS_GROUP) are defined in the file windows.h and not part of the resource script language. The C/C++ style #include directive is used to specify files that contain macro definitions.
Figure 10.1. Sample dialog box.
Generally, a resource file statement consists of an identifier that can either be a text string (DlgBox above) or a numeric value, followed by the statement itself. The statement can be single line (in which case, the statement is followed by one or more parameters) or multiline (in which case the statement is followed by a block of script instructions).
Preprocessor syntax and semantics are similar to the syntax and semantics of the C/C++ preprocessor. The resource compiler preprocessor, or RC preprocessor for short, understands the following directives:
The meaning of these directives is familiar to even the novice C programmer.
The preprocessor also understands (and removes) C and C++ style comments; that is, comments enclosed between /* and */ and comment lines that begin with //.
The preprocessor also understands the "stringizing" operator # and the "token-pasting" operator ##.
It is possible to use the same header file from both C source and resource script files. During resource compilation, the resource compiler defines the symbol RC_INVOKED; this symbol can be used in the header file to protect against compiler errors. For example, if the header file contains a class declaration, you may wish to protect it as follows:
#ifndef RC_INVOKED class myclass { ... }; #endif // RC_INVOKED
Another useful symbol to remember is the symbol APSTUDIO_INVOKED. This symbol is defined when a resource file is loaded by the integrated resource editor in Developer Studio. (In the days of Visual C++ 1.0, resource editing was performed by a separate program, Application Studio. Hence, the name of this constant.)
Resource scripts can also contain constant expressions involving the following operators: addition (+), subtraction (-), multiplication (*), division (/), unary not (~), binary and (&), and binary or (|).
Single-line statements define bitmaps, cursors, fonts, icons, message tables, and the language of resource statements.
The BITMAP statement specifies a bitmap file (edited separately with a bitmap editor) that is to define a bitmap resource. For example:
MyBitmap BITMAP mybitmap.bmp
The CURSOR statement specifies a file that defines the shape of the mouse cursor. The cursor file is a binary file that is edited by a separate editor. Here is an example for this statement:
MyCursor CURSOR mycursor.cur
The FONT statement specifies a font resource. An example:
MyFont FONT myfont.fnt
The ICON statement specifies a binary icon file (edited by an icon editor) that defines an icon resource. For example:
MyIcon ICON myicon.ico
The LANGUAGE statement sets the language of all subsequent resources up to the end of the resource script or until another LANGUAGE statement is encountered. The LANGUAGE statement can also be used to set the language of a specific resource in a multiline resource statement if it appears just before the BEGIN keyword in such a statement. The LANGUAGE statement is followed by a language and a sublanguage identifier, both of which must be constants defined in the file winnls.h.
The MESSAGETABLE statement identifies a message table, which is used primarily in Windows NT event logging. A message table is created by the message compiler, mc.exe.
The BITMAP, CURSOR, FONT, and ICON statements also accept an attribute parameter. The attribute parameter specifies load and memory characteristics of the resource. In 32-bit Windows, only one attribute is used: the DISCARDABLE attribute specifies that a resource can be removed from memory if it is no longer needed. For example:
TempBmp BITMAP DISCARDABLE "c:\\bitmaps\\tempbmp.bmp"
Multiline resource statements define dialogs, string tables, accelerator tables, menus, and version information resources. Multiline statements begin with an identifier, the statement, and optional parameters, followed by a block of instructions enclosed between a BEGIN-END keyword pair:
identifier STATEMENT [optional-parameters] [optional instructions] BEGIN [instructions] END
Optional instructions may include the CHARACTERISTICS instruction (specifies a single 32-bit value used only by resource file management tools), the LANGUAGE instruction, and the VERSION instruction (specifies a 32-bit version number used by resource management tools). Other optional instructions are multiline statement-specific; for example, the CAPTION instruction defines the title of a dialog box.
Because of their relative complexity, we'll look at multiline statements individually.
Accelerators are keystrokes that represent shortcuts to specific tasks. For example, when you use Ctrl+C to copy an item to the clipboard, you are using an accelerator.
Enclosed between the BEGIN-END keyword pair, an accelerator statement contains an arbitrary number of keyboard events followed by the identifier of the accelerator. Consider the following example:
MyAcc ACCELERATOR BEGIN "C", ID_EDIT_COPY, VIRTKEY, CONTROL, NOINVERT "V", ID_EDIT_PASTE, VIRTKEY, CONTROL, NOINVERT "X", ID_EDIT_CUT, VIRTKEY, CONTROL, NOINVERT END
This example assumes that the symbolic constants ID_EDIT_COPY, ID_EDIT_PASTE, and ID_EDIT_CUT are defined elsewhere (in a header file) and refer to numeric identifiers.
Accelerators are interpreted when an application calls the TranslateAccelerator function after retrieving a message through GetMessage (or PeekMessage). TranslateAccelerator translates WM_KEYDOWN (or WM_SYSKEYDOWN) messages into WM_COMMAND (or WM_SYSCOMMAND) messages. The identifiers that follow accelerator keys in an accelerator statement will become the command identifiers in the WM_COMMAND messages.
Together with menu statements, dialog statements define what are perhaps the most visible parts of a typical application. A dialog statement defines the layout and appearance of a dialog box.
A sample dialog statement is shown in Listing 10.1. The statement consists of several lines. The first line contains an identifier, the DIALOG keyword, and four numeric parameters that define the position of the dialog's upper-left corner and the size of the dialog box.
All size and position information in a dialog statement is measured in dialog units. Dialog units are derived from the size of the font specified for the dialog. Dialog base units represent the average height and width of a character in the selected font. Four horizontal dialog units amount to one horizontal dialog base unit; eight vertical dialog units amount to one vertical dialog base unit.
For dialogs that use the system font, applications can obtain the size of dialog base units, in pixels, by calling GetDialogBaseUnits. For dialogs using other fonts, it may be necessary to explicitly calculate the average size of characters to obtain the dialog base units.
Once the dialog base units are known, applications can convert between dialog units and pixels using the following formulae:
pixelX = (dialogunitX * baseunitX) / 4 pixelY = (dialogunitY * baseunitY) / 8 dialogunitX = (pixelX * 4) / baseunitX dialogunitY = (pixelY * 8) / baseunitY
Following the line containing the DIALOG keyword, a dialog statement may contain several optional instructions. These may include commonly used optional instructions or dialog-specific optional instructions.
The CAPTION optional instruction is followed by a string specifying the title of the dialog box. The default is a dialog with no title.
The STYLE optional instruction specifies the style of the dialog. Style values are usually predefined in the header file windows.h. Several values can be combined using the logical or (|) operator. The default style for dialogs containing no STYLE instruction is WS_POPUP | WS_BORDER | WS_SYSMENU.
The EXSTYLE optional instruction is similar to STYLE and specifies extended styles.
The CLASS optional instruction can be used to specify a special window class for a dialog. Using the CLASS instruction should be used sparingly, as it redefines the behavior of the dialog.
The FONT statement specifies the font to be used in the dialog. The default is the system font.
The MENU statement identifies the menu resource that defines the menu of the dialog box. In the absence of this statement, the dialog box will be created without a menu bar.
Enclosed between the BEGIN-END keyword pair is a series of control statements specifying the controls within the dialog. There are several types of control statements. Each control statement contains the control type, control text, a control identifier (text or integer), control position, and control style and extended style parameters:
An edit control is defined by the EDITTEXT control statement.
A static control is defined by the LTEXT, CTEXT, RTEXT, or ICON control statement. The first three of these control statements define a left-aligned, centered, or right-aligned static control, respectively. The last specifies a static control with the SS_ICON style.
A button control is defined by one of the following keywords: AUTO3STATE, AUTOCHECKBOX, AUTORADIOBUTTON, CHECKBOX, DEFPUSHBUTTON, GROUPBOX, PUSHBOX, PUSHBUTTON, RADIOBUTTON, STATE3, USERBUTTON.
The COMBOBOX control statement defines a combo box control.
The LISTBOX control statement can be used to specify a list box.
The SCROLLBAR control statement defines, what else? A scrollbar.
The CONTROL control statement can be used to define a generic control. The syntax of this statement is somewhat different from the syntax used for other control statements:
CONTROL control-text, identifier, class-name, x, y, width, height [, extended-style]
The class-name parameter specifies the window class for the control. This can be one of the Windows control classes. Thus, the CONTROL statement can be used as an alternative syntax for all the other control statements.
A variant of the DIALOG statement is the DIALOGEX statement. It extends the syntax of the DIALOG statement in the following ways:
Menu statements in the resource script can be used to specify menu bars or popup menus. Menu statements contain one or more menu definition statements enclosed between a BEGIN-END keyword pair. Consider the following example:
MyMenu MENU BEGIN POPUP "&File" BEGIN MENUITEM "&New\tCtrl+N", ID_FILE_NEW MENUITEM SEPARATOR MENUITEM "E&xit", ID_FILE_EXIT END POPUP "&Edit" BEGIN MENUITEM "Cu&t\tCtrl+X", ID_EDIT_CUT MENUITEM "&Copy\tCtrl+C", ID_EDIT_COPY MENUITEM "&Paste\tCtrl+V", ID_EDIT_PASTE END POPUP "&Help" BEGIN MENUITEM "&About", ID_HELP_ABOUT END END
The identifiers (for example., ID_FILE_NEW) in this example are assumed to be defined elsewhere and refer to numeric values.
A menu definition statement can specify a menu item or a submenu. To define a menu item, use the MENUITEM keyword. This is followed either by the text of the menu item and the menu identifier or by the SEPARATOR keyword; the latter specifies a separator that is a vertical line for menu bars or a horizontal line for popup (sub) menus.
A menu item's identifier may be followed by a list of options. Options are separated by commas or spaces and include the following keywords: CHECKED, GRAYED, HELP, INACTIVE, MENUBARBREAK, MENUBREAK.
To specify a submenu, use the POPUP keyword. The POPUP keyword is followed by the title of the submenu, and a set of menu items enclosed between a BEGIN-END keyword pair. A submenu may contain nested submenus.
A string table resource defines an arbitrary number of text strings. These text strings can be referred to from within the application by symbolic identifiers. The primary advantage of using a string table resource is that it makes it possible for all language-dependent components of an application to be conveniently located within a resource file and thus renders the task of localization substantially easier.
A string table is defined by the STRINGTABLE keyword, followed by optional instructions, and one or more string definitions enclosed between a BEGIN-END keyword pair. A string definition consists of a string identifier and a string value. For example:
STRINGTABLE BEGIN IDS_HELLO "Hello" IDS_GOODBYE "Goodbye" END
where IDS_HELLO and IDS_GOODBYE are symbolic identifiers defined elsewhere.
Using a string from a string table resource is straightforward. An application can load a string resource using the LoadString function.
For Microsoft Foundation Class applications, using string table resources is even easier. Many MFC functions that accept a string parameter can also accept a numeric parameter representing a string resource in the application's resource file. For example, an MFC application can display a message box using AfxMessageBox as follows:
AfxMessageBox(IDS_ERROR);
Here, IDS_ERROR is a symbolic reference to a numeric identifier.
The CString class also offers specific support for string resources. The member function CString::LoadString can be used to initialize a CString object to a value obtained from a string table in the application's resource file.
Toolbar resources are used in MFC applications. A toolbar is defined in a resource file by a TOOLBAR statement. This statement lists the identifiers of the buttons in the toolbar. The size of toolbar buttons is also specified, as in the following example:
IDR_MAINFRAME TOOLBAR DISCARDABLE 16, 15 BEGIN BUTTON ID_FILE_NEW BUTTON ID_FILE_OPEN BUTTON ID_FILE_SAVE SEPARATOR BUTTON ID_EDIT_CUT BUTTON ID_EDIT_COPY BUTTON ID_EDIT_PASTE SEPARATOR BUTTON ID_FILE_PRINT BUTTON ID_APP_ABOUT END
For every toolbar resource, there should be a corresponding bitmap resource that contains the button bitmaps. The button bitmaps should be arranged horizontally, in the same order as the button identifiers appear in the toolbar resource (except for separators). The bitmap resource should have the same identifier as the toolbar resource:
IDR_MAINFRAME BITMAP MOVEABLE PURE "res\\Toolbar.bmp"
Toolbar resources are accessed through the MFC function Ctoolbar::LoadToolBar.
The version information resource can be used to identify the version of a binary file (typically, an executable or library file). Version information is used by File Installation Library functions.
The version information resource statement contains several version statements that identify the version number of the file and the product, provide additional version information, and specify the language and the target operating system.
The resource file syntax also allows for the creation of user-defined resource types. A user-defined resource statement can be a single-line or multiline statement. Single-line statements are used to identify user-defined resources that are stored in separate files. The syntax of a single-line statement is as follows:
name type [load-memory-options] filename
Multiline user-defined resource statements can be used to embed the definition of a user-defined resource within the resource file. The syntax of a multiline user-defined resource statement is this:
name type [load-memory-options] BEGIN raw-data END
The raw-data block may contain integers in decimal, hexadecimal, or octal notation, or strings enclosed in double quotes. Raw data strings must be explicitly null-terminated. Individual data items are separated by commas.
Raw data can also be specified in the form of an RCDATA resource. The syntax of an RCDATA statement and the syntax of a multiline user-defined resource statement are nearly identical, except that in the case of raw data statement, the keyword RCDATA is used in place of type, and the statement may also contain the optional instructions CHARACTERISTICS, LANGUAGE, and VERSION.
Before a resource script can be used by an application, it must be compiled. Although not strictly necessary, in the case of most applications the compiled resource file is also linked with other application components and becomes part of the application's executable file image (.exe file).
A resource file can be compiled from the command line using the resource compiler, rc.exe. For most resource files, this command can be used with no parameters. For example, to compile the resource file shown in Listing 10.1, you would type rc dlg.rc.
In 16-bit versions of Windows, the resource compiler was also used to add resources to the executable file. In Win32, the 32-bit linker is used for this purpose.
The Visual C++ linker, link.exe, can be used to link a resource file and other components of an application's executable together. In the simplest case, you would specify the name of the compiled resource file on the C/C++ compiler command line just as you would specify any other object file or library file. For example:
cl dlg.cpp dlg.res user32.lib
In order for the linker to work properly with compiled resource files, the cvtres.exe tool must be present in the same directory as the linker executable, link.exe. This tool converts compiled resource files into the Common Object File Format (COFF) which is understood by the linker.
Resources do not need to be linked with your application's executable file. They can also be linked into a separate dynamic-link library. This has the advantage that changing the application's resource file does not require recompiling the entire application, only replacing the DLL. You can also ship your application with multiple DLLs representing resources in various languages. In MFC applications, this approach is made even easier by the AfxSetResourceHandle call; calling this function with a handle to a resource DLL ensures that subsequently, the MFC loads all resources from that DLL instead of your application's executable file.
Resource files define the visual appearance of an application. Resources include dialogs, menus, bitmaps, icons, cursors, text strings, and several other types.
While resources are typically created with graphical editors, it is possible (and sometimes necessary) to edit the resource file directly. Resource files (files with the .rc extension) are human readable ASCII text files.
Resource files contain preprocessor directives, single-line, and multiline resource statements. Preprocessor directives are similar in syntax and semantics to their C/C++ counterparts. Single-line resource file statements are used to identify resources stored in separate binary files (edited by specialized editors): bitmaps, icons, cursors, fonts, and generic resources. Single-line statements are also used to specify message table resources (used for Windows NT event logging) and to specify the language of subsequent resource statements.
Multiline statements are used to define menus, dialogs, string tables, accelerator tables, version information resources, and generic resources.
Resource files are compiled using the Resource Compiler, rc.exe. The resulting compiled resource file (usually a file with the .res extension) is suitable for linking with other components of the application. A compiled resource file can also be specified on the cl command line.