Click here to Skip to main content
Click here to Skip to main content

Using Resources in the VCF

, 19 Apr 2006
Rate this:
Please Sign up or sign in to vote.
An article on adding and using resources to your application using the VCF.

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:

  • \n
  • \r
  • \t
  • \\
  • \"
  • \'
  • \?

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:

<?xml version="1.0" encoding="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!

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Jim Crafton
Software Developer (Senior)
United States United States
Currently working on the Visual Component Framework, a really cool C++ framework. Currently the VCF has millions upon millions upon billions of Users. If I make anymore money from it I'll have to buy my own country.

Comments and Discussions

 
Generalrandom thoughts about VCF PinmemberRoland Pibinger25-Apr-06 7:36 
GeneralRe: random thoughts about VCF PinmemberJim Crafton25-Apr-06 7:55 
Well in your example that you posted, aside from having a global static class on the stack (which can cause potential problems, depending on what code get's executed in the constructor), I'm not sure what you gained? "theApp.createNewAppInstance" still implies a new instance is being created, plus what is "AppInstance" then for? Ditto for "theApp.crateNewWindow()", that still implies that a new window instance is being created, doesn't it? Plus it potentially introduces another layer of complexity? How do you create a different type of Window instance?
 
I don't see quite what the problem is, other than on first glance it might appear to be memory that wasn't reclaimed. It seems like an awfully small issue to prevent you from looking at the rest of the framework.
 
¡El diablo está en mis pantalones! ¡Mire, mire!
 
Real Mentats use only 100% pure, unfooled around with Sapho Juice(tm)!
 
SELECT * FROM User WHERE Clue > 0
0 rows returned

Save an Orange - Use the VCF!
QuestionWhat is VCF? PinmemberRudolf Jan Heijink22-Apr-06 0:11 
AnswerRe: What is VCF? Pinmembertareqsiraj23-Apr-06 20:13 
AnswerRe: What is VCF? PinmemberJim Crafton24-Apr-06 9:45 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.141022.1 | Last Updated 19 Apr 2006
Article Copyright 2006 by Jim Crafton
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid