Symbian
Symbian Developer Library

SYMBIAN OS V9.4

Feedback

[Index] [Previous] [Next]


How to support multiple resource files

An application often needs to use resources from more than one resource file. A common situation involves a system resource file for environment resources and an application resource file for the application specific resources.

In general, it is useful for an application to be able to use an arbitrary number of resource files.

The resource compiler and the RResourceFile class provide two mechanisms to support these requirements:

These ideas are explained below:

[Top]


Name, version and signature

The following resource file definition uses the NAME statement and a signature.

NAME BASE

#include "xxx.rh"

STRUCT SIGNATURE
{
LONG signature;
SRLINK self;
}

RESOURCE SIGNATURE {}

RESOURCE STRING r_base_hello
{
text=Hello World!;
}

The STRING struct is defined in the resource header file xxx.rh:

STRUCT STRING
{
TEXT text;
}

After resource compilation, the generated .rsg header file contains:

#define R_BASE_HELLO 0x9EA5002

The name BASE causes an offset, 0x09ea5000, to be added to every resource id in the file.

In order to provide a guaranteed way to find out what the offset is, the resource file must begin with a SIGNATURE resource. This resource contains two 32-bit fields: a version number and a self-referencing link. Because the resource is the first in the file, the self-referencing link refers to resource 0x09ea5001. The RResourceFile::ConfirmSignatureL() function reads the signature resource and initialises the offset value from this resource. RResourceFile::ConfirmSignatureL() must always be called before attempting to read resources from a file, otherwise subsequent attempts at reading resources will fail.

_LIT(KZSystemDataFile1Rsc,"Z:\\system\\data\\File1.rsc");

RResourceFile resourceFile;

    // Opens file, leaves on error
resourceFile.OpenL(fsSession,KZSystemDataBasigbRsc); 

    // Initialise offset value from the first resource.
    // Note that the function is prototyped to take a TInt but does not use it.
resourceFile.ConfirmSignatureL(0));

Note that a standard signature struct can be found in badef.rh and uikon.rh.

[Top]


Using multiple resource files

The R_BASE_HELLO resource can be used in another resource by including the generated .rsg file in the new resource definition. Assuming that File1.rsg is the generated .rsg file from the earlier resource compilation, the new resource is defined:

NAME USER
#include xxx.rh    // include for common structures
#include File1.rsg // include for generated symbols
STRUCT STRINGREF
{
LLINK stringlink;
}
RESOURCE SIGNATURE {} // signature
RESOURCE STRINGREF r_user_helloref
{
stringlink=R_BASE_HELLO;
}

This resource file has the name USER. It has a signature resource, followed by a single real resource, of type STRINGREF, which contains an LLINK to the R_BASE_HELLO resource.

Note that common struct definitions are placed in .rh files and are included in all resource definition files (.rss files) that need them. Another #include is needed for the .rsg file so that the R_BASE_HELLO symbol is available to this compilation.

This resource definition has a dependency on the earlier resource definition. The earlier resource must be compiled first and its generated .rsg file must be available to the second resource.

After resource compilation, the generated .rsg header file contains:

#define R_USER_HELLOREF 0x68553002

Note that the offset for this resource file is 0x68553000.

[Top]


Reading resources from multiple files

In general, an application program may wish to use resources from several files simultaneously.

It is convenient to be able to treat all resource files as a single resource container. To do this, an application must open each resource file and call RResourceFile::ConfirmSignatureL() to initialise the offset value for that resource file.

When a resource is needed, the application decides which resource file owns that resource by calling RResourceFile::OwnsResourceId(). This function matches the resource file's offset value with the offset value contained in the resource id.

CMultipleResourceFileReader is an example class that behaves as a container for a collection of resource files:

class CMultipleResourceFileReader
    {
public:
    // construction and destruction
    ~CMultipleResourceFileReader(); 
    static CMultipleResourceFileReader* NewLC();

    // add resource file
    void    AddResourceFileL(const TDesC& aName);
    HBufC8* AllocReadLC(TInt aResourceId);

private:
    // construct/destruct
    void ConstructL();

private:
    CArrayFixFlat<RResourceFile>* iResourceFiles;
    };

The iResourceFiles data member points to an array of resource files. Resource files are added to the collection through the AddResourceFileL() function.

void CMultipleResourceFileReader::AddResourceFileL(const TDesC& aName)
{
RResourceFile file;
file.OpenL(fsSession,aName);              // open resource file and initialise its
TRAPD(error,file.ConfirmSignatureL(0));   // offset value.
if (error!=KErrNone)
    {
    file.Close();
    User::Leave(error); 
    }
TRAP(error,iResourceFiles->AppendL(file)) // Add the resource file to the collection
if (error!=KErrNone)
    {
    file.Close();
    User::Leave(error);
    }
return;
}

The CMultipleResourceFileReader::AllocReadLC() function performs the same function as RResourceFile::AllocReadLC(), but it first scans through its collection to find the resource file which owns the resource:

HBufC8* CMultipleResourceFileReader::AllocReadLC(TInt aResourceId)
{
// Scan all resource files to find owner
for (TInt i=0; i < iResourceFiles->Count(); i++)
    { 
    RResourceFile& file=(*iResourceFiles)[i];
    if (!file.OwnsResourceId(aResourceId))
        {
        continue;       // Owner not yet found
        }
    return file.AllocReadLC(aResourceId); // Return resource from owning file
    }

// Resource owner not found.
User::Leave(KErrNotFound);

// Never executed, but keeps compiler happy
return 0;
}

When the owning resource file is found, the resource is looked up in that file. If the correct owner cannot be found or the resource cannot be found in the owning file, then the function leaves with an error indication.

The following code fragment uses the CMultipleResourceFileReader class to access the R_SOME_RESOURCE resource that is defined in a resource file File2.rsc and to construct an instance of some class CMyClass from this resource.

CMultipleResourceFileReader* multiReader = CMultipleResourceFileReader::NewLC();

// Add some resource files
multiReader->AddResourceFileL(_L(Z:\\system\\data\\File1.rsc));
multiReader->AddResourceFileL(_L(Z:\\system\\data\\File2.rsc));
multiReader->AddResourceFileL(_L(Z:\\system\\data\\File3.rsc));

// Read the resource. 
HBufC8* refBuffer = multiReader->AllocReadLC(R_SOME_RESOURCE);
TResourceReader theReader;
theReader.SetBuffer(refBuffer);

// construct a CMyClass object from this resource.
CMyClass* resData = CMyClass::NewLC(theReader);

...