Introduction
Most Win32 apps use some form of resource, such as dialogs, string tables, images (bitmaps), icons, and so forth. This article will show you how to use resources in a VCF based program.
The VCF sees a resource as a chunk of binary data. This data may be manipulated as a raw byte stream, a string, or an image. Support for what the VCF refers to as "Visual Form Files" is also present but the data itself is still just plain text.
The VCF provides access to resources through a class called ResourceBundle
which wraps up the low level details of actually getting at the resource data. There is a single ResourceBundle
per application or dynamic library. The base class for the ResourceBundle
only deals with resources as raw binary data or text, but the GraphicsResourceBundle
adds support for image resources as well.
The ResourceBundle
implements its functionality using its peer class. The peer class is specific to each platform, and it implements the details of extracting the resource data through calls like FindResource
and LoadString
.
Traditionally, you would store your resource data in a combination of one or more .rc scripts and possible external files for things like images and what not. These would then get compiled and linked into the final executable. The VCF ResourceBundle
is perfectly capable of accessing resources built in this manner, however, it can also work with resources that are stored as a Bundle. Bundles are directory structures that define where various resource files are, and instead of being bound directly to the executable, they are simply re-distributed with the program. The idea for this was lifted from the OS X concept of bundles, with the addition of a slightly more flexible directory structure. So, with the VCF, you have a choice of how to implement your resource storage, but from a developer perspective, you don't have to care - the ResourceBundle
will check using the native APIs first (like LoadString
, FindResource
, etc.), and if it can't locate the resource this way, it will attempt to load the resource using the bundle mechanism.
Bundles can be laid out something like this:
App Directory/
MyApp.exe
Resources/
MyApp.strings
img1.png
img2.bmp
notes.txt
readme.txt
You can have more complex layouts that can support localized resources as well. You simply create sub directories under the "Resources" directory, one for each locale, using the standard ISO language and country names. In each directory, you then place the appropriately localized resource files. For example, this would support US English, Italian, and Polish:
App Directory/
MyApp.exe
Resources/
en_US/
MyApp.strings
img1.png
img2.bmp
notes.txt
readme.txt
it_IT/
MyApp.strings
img1.png
img2.bmp
notes.txt
readme.txt
pl_PL/
MyApp.strings
img1.png
img2.bmp
notes.txt
readme.txt
When you request a resource, the VCF will check the current thread's locale, and then will check for the presence of a localized resource directory.
Usage
Using the ResourceBundle
is quite easy. First, you need to get access to it since you do not create an instance directly, that's managed by the framework. To access it, you just call:
<A href="http://vcf-online.org/docs/src_manual/classVCF_1_1System.html#f3509f91121b73b62c9c9cefac1226b1" target=_blank>System::getResourceBundle</A>
or if you have a an application that's using the ApplicationKit
:
<A href="http://vcf-online.org/docs/src_manual/classVCF_1_1Application.html#18fbf7e76dface3fc10fc75ff65e49d7" target=_blank>Application::getRuningInstance()</A>-><A href="http://vcf-online.org/docs/src_manual/classVCF_1_1AbstractApplication.html#2f6d56a7f993c746659d30771d08b706">getResourceBundle()</A>
Once you have a ResourceBundle
, you can call the following functions:
If you're using the GraphicsKit
or ApplicationKit
, you can also call GraphicsResourceBundle::getImage()
to access images. Images may be stored in any format that is currently understood by the framework. The default formats are JPEG and PNG, and under Windows, there's also support for BMP.
Accessing Raw Resource Data
You can access raw resource data by calling the getResource()
function of the ResourceBundle
instance. This will return an instance of the Resource
class that allows you access to this raw data. The Resource
class has functions that return the number of bytes in the resource, a pointer to the data, and the name of the resource. You would access this like so:
Resource* resource =
System::getResourceBundle()->getResource( "some-resource" );
if ( NULL != resource ) {
void* dataPtr = resource->getData();
String name = resource->getName();
uint32 dataSize = resource->getDataSize();
}
The framework will look first in the executable's resources, and then resort to looking for the resource using the bundle API. When it searches the executable's resources, it stops on the first resource whose name matches the requested resource name. Any resource can cause a match, the search does not look at the resource type. This means that your resource could be a BMP, some form of RCDATA
, or something else entirely. On Windows, the search is executed using the EnumResourceTypes
API function.
Any resource specified in a .RC script may be accessed this way, so long as it's not a string in a string table and doesn't have a duplicated name.
Storing the Resource in a Bundle
Resources in a bundle may be accessed in this way, so long as the name used to identify the resource is the file name. For example:
Resource* resource =
System::getResourceBundle()->getResource( "stuff.dat" );
if ( NULL != resource ) {
void* dataPtr = resource->getData();
String name = resource->getName();
uint32 dataSize = resource->getDataSize();
}
Assuming the file stuff.dat exists in the proper directory, the resource returned will be the data from the file. Some possible directory structures that you can use:
App Directory/
MyApp.exe
Resources/
stuff.dat
App Directory/
Contents/
MyApp.exe
Resources/
stuff.dat
App Directory/
Contents/
(OS Name)/
MyApp.exe
Resources/
stuff.dat
The VCF will work with any of these patterns. The last two are 100% compatible with OSX. The first is the simplest approach.
One thing to note: it's completely acceptable to have resources stored both in the executable (using RC scripts) and stored as files in a bundle directory tree. So long as the resource names are unique, no conflicts will arise. If the names are not unique, preference will be given to matching resources that are native executables bound over bundle based resources.
Accessing Text Resources
To access a string in a resource, we would do the following (assuming that we are just using the FoundationKit
):
#define RES_NAME "test"
String resStr = System::getResourceBundle()->getString( RES_NAME );
System::println( resStr );
As we mentioned earlier, you can store the resource in a number of ways, either as regular files that the framework reads in, or, if you are on Windows, through the use of .rc scripts. Let's look at how we could represent the data first in a resource script, and then look at how to do so using bundles and plain files.
Storing the Text in an RC file
The simplest way to store the data in an RC script is using the RCDATA
resource type. The previously mentioned string would be stored like so:
test RCDATA
BEGIN
"A Test String"
END
where "test" is the resource name, "RCDATA" is the resource type, and "BEGIN" and "END" are used to notate where the resource data begins and ends respectively. You can put your string in quotes. If this is compiled and linked to your executable, the VCF will then be able to load this data at runtime.
An alternate approach is to use a string table. This is frequently how strings are stored in Win32 or MFC based applications. String table entries are notated like so:
STRINGTABLE DISCARDABLE
BEGIN
IDS_STRING1 "A Test String"
END
Unlike the RCDATA
, the ID value for the string is an integer, and the value for that must either be defined directly in the rc script, included from another file (like a header), or the ID number must be used directly. In our case, if we replace "IDS_STRING1
" with a number, then we don't have to worry about including a resource header, or defining the symbol. So our resource file becomes:
STRINGTABLE DISCARDABLE
BEGIN
1 "A Test String"
END
The framework is able to make use of a string table based resource assuming no other match is found for the resource name. In this case, the Windows code will then assume that the resource name is a number, and will attempt to convert it to an integer, and then call the Win32 LoadString()
function, which will extract the string from the string table resource. So if we change our code to:
#define RES_NAME "1"
String resStr = System::getResourceBundle()->getString( RES_NAME );
System::println( resStr );
resStr
will be assigned "A Test String" from the executable's string table.
Storing the Text in a .strings file
The last way to store a string is to use a .strings file and the previously mentioned bundle directory layout. This file is always named whatever your executable is named plus the ".strings" extension. So if you have a program called "FooBar.exe", then the associated .strings file would be named "FooBar.strings". The .strings file is a plain text file, with any number of entries, where an entry is a key or ID name in double quotes, followed by the "=" character, and then the entry value string, again in double quotes. For example, the contents of our FooBar.strings file might be:
"test" = "A Test String"
The same code we previously used would also work with this, allowing us to get rid of the code in the RC script. An advantage with this approach is that it's easy to localize as well.
The text of both the ID (or resource name) and the value may contain any characters you want. Escape characters supported are:
Hexadecimal, octal, and Unicode escape sequences are also supported in the form:
- \x followed by exactly three hexadecimal digits, such as \x0FD. These may be 0 padded, if necessary.
- \XXX three octal digits, such as \123.
- \U followed by exactly four hexadecimal digits, such as \U043E. These may be 0 padded, if necessary.
Accessing Image Resources
Image resources are accessed much the same way as binary or text resources are. The only difference is that you need to link to the GraphicsKit
(or ApplicationKit
) to have access to these features. Image resources are made available through the GraphicsResourceBundle
class. The GraphicsResourceBundle
is accessed like so (it's also a singleton):
GraphicsResourceBundle* bundle =
GraphicsToolkit::getResourceBundle();
This returns the resource bundle for the executable, just like System::getResourceBundle()
does.
Once you have the resource bundle, you can get the image resources:
Image* image = bundle->getImage( "someImageName" );
This will return you a new instance (which you're responsible for freeing at some later point in time) of an image, or NULL
if no resource could be found by the name specified.
Like text and binary data, images may be either stored in the executable, or as external files. Let's look at how to store them in a RC script first.
Storing Images in an RC file
If you store an image in an RC file, you're limited to bitmap (.BMP), icon (.ICO), or cursor (.CUR) types. To add these to your script, you add a line like this:
play BITMAP "play.bmp"
The first item is the resource name, the next is the resource type (a BITMAP
, ICON
, or CURSOR
), and the last is the name of the file relative to where the RC script is, to compile into the executable.
Storing Images Externally
Another way to store images is to store them as external files using the previously mentioned bundle directory layout.
Accessing Resources from Dynamic or Shared Libraries
Up till now, we've assumed that the source of the resource bundle is the executable. However, it is entirely possible to have the resources bound to a DLL, particularly if the DLL is a plug-in of some sort. To do this, you need to create your DLL as normal. You can add resources as normal to your DLL's RC script. Then, in your DLL's start up code, you need to create a single instance of the LibraryApplication
class. This will represent your loaded DLL and provide access to your DLL's resources. Once you've created the instance, you need to give it a name, and then register the LibraryApplication
instance. From there on, you can access this instance by calling the LibraryApplication::getRegisteredLibraryApplication()
and passing in the appropriate library name. An example of this:
extern "C" __decspec(__dllexport) void _vpl_init(OSHandleID handle)
{
LibraryApplication* libApp = new VCF::LibraryApplication();
libApp->getPeer()->setHandleID( handle );
libApp->setName( "MyLib" );
LibraryApplication::registerLibrary( libApp );
}
extern "C" __decspec(__dllexport) void _vpl_terminate(OSHandleID handle)
{
LibraryApplication* libApp =
LibraryApplication::getRegisteredLibraryApplication( "MyLib" );
if ( NULL != libApp ) {
LibraryApplication::unRegisterLibrary( libApp );
libApp->free();
}
}
We export two functions, one that the framework calls whenever a DLL is loaded up via the Library
class, and the other which the framework calls when the DLL is unloaded. You could also put the code in DllMain
if you want to.
The first function, _vpl_init()
, creates the LibraryApplication
instance, sets the peer handle for the library, and sets the name of the library. The second function, _vpl_terminate()
, retrieves the library instance, un-registers it, and then deletes it.
To access a resource from the library app or DLL, you simply get an instance of the LibraryApplication
you're interested in, and then call the LibraryApplication::getResourceBundle()
, for example:
LibraryApplication* libApp =
LibraryApplication::getRegisteredLibraryApplication( "MyLib" );
Image* img =
libApp->getResourceBundle()->getImage( "donald-duck" );
Program Information as a Resource
The last type of resource available in the VCF is program information. Basically, it's a collection of various strings that describe the executable, such as the name of the executable, its version, author, copyright, etc. You can access this information by calling the ResourceBundle::getProgramInfo()
function. If there is no ProgramInfo
available, then ResourceBundle::getProgramInfo()
will return NULL
. For example:
ProgramInfo* info = System::getResourceBundle()->getProgramInfo();
if ( NULL != info ) {
System::println( info->getProgramName() );
System::println( info->getAuthor() );
System::println( info->getCopyright() );
}
The program information can be provided in two ways, at least on Windows. The first way is to use the information already available in the VS_VERSION_INFO
resource type that can be embedded into an RC script. For example:
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,0,0,1
PRODUCTVERSION 1,0,0,1
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x40004L
FILETYPE 0x1L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "Comments", "\0"
VALUE "CompanyName", "Nose Picker's Anonymous Inc.\0"
VALUE "FileDescription", "VersInfo\0"
VALUE "FileVersion", "1, 0, 0, 1\0"
VALUE "InternalName", "VersInfo\0"
VALUE "LegalCopyright", "Copyright ? 2006\0"
VALUE "LegalTrademarks", "\0"
VALUE "OriginalFilename", "VersInfo.exe\0"
VALUE "PrivateBuild", "\0"
VALUE "ProductName", "Nose Picker's Anonymous Inc. VersInfo\0"
VALUE "ProductVersion", "1, 0, 0, 1\0"
VALUE "SpecialBuild", "\0"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END
This information can be extracted and made available to the ProgramInfo
instance.
The second way to provide this information is with an external file. This file is placed at the same level as the program's "Resources" directory, and is named either "Info.xml" or "Info.plist". The idea is to store similar information in an XML based format, which is then read by the ResourceBundle
. The format of the file looks something like this:
="1.0"="UTF-8"
<plist >
<dict >
<key >
Executable
</key>
<string >
VersInfo.exe
</string>
<key >
ProgramVersion
</key>
<string >
1.0
</string>
<key >
FileVersion
</key>
<string >
1.0
</string>
<key >
ProductName
</key>
<string >
VersInfo
</string>
<key >
Copyright
</key>
<string >
Copyright ? 2006
</string>
<key >
Author
</key>
<string >
Jim Crafton
</string>
<key >
Company
</key>
<string >
Nose Picker's Anonymous Inc.
</string>
</dict>
</plist>
Notes on Building the Examples
You'll need to have the most recent version of the VCF installed (at least 0-9-0 or better), and you'll need to make sure you have built the static libraries for the VCF (as opposed to the DLL version). The examples are configured to link to the VCF statically. For more documentation on building the VCF, see: Building the VCF, at the VCF online documentation.
Conclusion
We've covered all the basic resource operations, and shown how to extract images, files, raw binary data, and text.
Questions about the framework are welcome, and you can post them either here or in our forums. If you have suggestions on how to make any of this better, we'd love to hear them!