A few questions seem to pop up over and over again on The Cobb Group's CPB-Thread list. For example, "Is there another resource editor for C++Builder?" The answer is, "Many--but you don't often need them." (Participation on CPB-Thread is a useful way to keep up with C++Builder issues and learn about its diverse range of features. To subscribe, visit www.cobb.com/cpb.)
If you're using Borland C++ 4.5, you already have a copy of Resource Workshop. (If you use version 5, it's built into the IDE.) In fact, any editor that will produce RC files will do--and such editors are readily available. For example, search a Delphi Super Page mirror for Triplex++, a freeware package by John Howe. Triplex includes a stand alone resource editor and a great Grep utility.
However, look closely at the resources you use most often, and you'll find you already have the tools you need--C++Builder does most of the work for you. It can edit dialog boxes and menus better than any other utility. The image editor is fairly basic, but it can create bitmaps, cursors and icons. That leaves string tables, version information, user defined resource objects, and such items as fonts and audio wave files. You'll find that these remaining resources are easy to create manually, if you know how.
Included with C++Builder is the RCC32 resource script compiler, which can compile any standard RC file. You don't have to do anything special to use the resource compiler--simply add any resource script to your project and C++Builder will automatically compile it and bind it to your DLL or EXE.
In this article, we'll make and use some resources. We'll start with a bit of background information, then we'll build a string table and add version information. Finally, we'll add a wave and play it. You can download our sample files from www.cobb.com/cpb as part of the file dec97.com; click the Source Code hyperlink. (Note that the resource part was so easy that the example was too boring--I jazzed it up with a splash screen, volume controls, and a demonstration of the three different ways to use PlaySound().) Let's get started.
Resource scripts support several preprocessor directives: #define, #include, #if/else/endif, #ifdef/ifndef, and #undef are all available. If you want to add some notes to your script, you can use the old C-style comments (/*...*/).
You can identify resources using a name, but the code runs faster if you use a unique numeric ID, instead. It's common practice to #include in your project and in your script a header that defines ID numbers. Doing so makes it easier to change the ID numbers later (in case of a collision with another resource ID).
/* resource header file */ #define IDS_NOWAVE 1001 #define IDS_SOUNDOFF 1002And the following lines illustrate a resource script:
#include "resources.h"
Welcome Wave "welcome.wav"
Splash Bitmap "splash.bmp"
stringtable
begin
IDS_NOWAVE, "No wave devices"
IDS_SOUNDOFF, "Sorry - Sound Disabled"
end
As
you can see, each string resource consists of nothing more than an ID for each
string, a comma, and the string itself. To use one of these strings in your
code, load it using the LoadStr() function provided by VCL, as follows:Edit1->Text = LoadStr( IDS_NOWAVE );The LoadStr() function searches the currently executing task for a string table resource with the specified numeric ID. It returns an AnsiString result. If the resource isn't found, the function returns a null string.
Listing A: Version Information in Version.Rc
VERSIONINFO_1 VERSIONINFO
FILEVERSION 1, 2, 3, 4 /*file vers#, 4 parts*/
PRODUCTVERSION 1, 5, 6, 7 /* product vers#*/
FILEOS VOS_NT_WINDOWS32 /* WIN 32/NT */
FILETYPE VFT_APP /* use VFT_DLL for a DLL */
{
BLOCK "StringFileInfo" /* Don't Touch This! */
{
BLOCK "040904E4" /* Don't Touch This! */
{
/* edit this for your app */
VALUE "CompanyName", "Sam Azer\000\000"
VALUE "FileDescription", "PlayResource Demo
shows how to make a resource file by
hand (and use the resources!)\000"
VALUE "FileVersion", "1.2.3.4\000\000"
VALUE "InternalName", "PlayRes\000"
VALUE "LegalCopyright", "Copyright \
251 1997\000\000"
VALUE "OriginalFilename", "PlayRes.exe\000"
}
}
BLOCK "VarFileInfo"
{
/* U.S. English, Windows ANSI code page */
VALUE "Translation", 0x409, 1252
}
}
Your file and product version numbers can consist of up to four parts. If you
don't need that many, set the unused values to zero. C++Builder will only
produce code that runs under Windows 95 or NT, so you don't have to change the
FILEOS value. If your project output is a DLL, change the FileType from VFT_APP
to VFT_DLL. Don't touch the two block headers--Windows looks for them. The two
Translation values at the bottom are Language and Code Page; the
values shown are for U.S. English under the U.S. ANSI code page for Windows.
Other values are listed in the Win32 Reference.
After you've built your project, you can view the version information from the Windows Explorer. Use the Explorer to find your project's EXE or DLL file. Right-click on the file, choose the Properties option at the bottom of the speed menu, then click on the Version tab.
id type [PRELOAD] filenameThe ID can be a unique resource number or a name. Type is a string that describes the resource. There are a few standard types, such as BITMAP, CURSOR, ICON, and FONT, or you can make up your own. The PRELOAD option forces Windows to load the resource right away, which can be handy if you want to avoid delays with an often-used resource. If you use the following code in a form's OnPaint method, it will load and draw the sample bitmap Splash.BMP:
TBitmap *Splash = new Graphics::TBitmap; Splash->LoadFromResourceName( (int)HInstance, "Splash" ); // Set the form size to match the splash image ClientWidth = Splash->Width; ClientHeight = Splash->Height; Canvas->CopyMode = cmSrcCopy; Canvas->Draw( 0, 0, Splash );Note the use of HInstance, a global variable provided by VCL that gives the handle for the currently executing task. Windows searches that EXE or DLL file for the requested resource. The next line of code will play the sample wave file Welcome.WAV. Take a look:
PlaySound("Welcome", HInstance, SND_RESOURCE);
What about all those resources that Windows doesn't understand? For
user-defined data, the standard resource type is RCDATA. If you want to include
the data in the resource script, it will look like this:MyResource RCDATA Begin "this is a string\000" /* note the null */ 1,2,3,4 /* integers */ 0x01, 0x02, 0x03, 0x04 /* Hex ints */ '01', '02 03 04' /* Hex bytes */ EndWindows provides five functions to handle user defined resources. FindResource()finds any type of resource, LoadResource()brings it into memory, and LockResource()returns a pointer to it and makes it stay put. SizeofResource() returns the size of a resource in bytes.
Finally, you don't have to get all your resources out of the currently executing application--you can use the LoadLibrary() function to get an HInstance handle for any DLL or EXE. Check the Win32 Platform SDK help files for details. Note that if you get an error return value after calling one of these functions, you can use the GetLastError() function to find out what went wrong. Use VCL's SysErrorMessage() function to translate the information into English.