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:
the NAME
statement allows a four-letter short name
to be defined for a source file; this name is converted into a 20-bit value,
represented by five hex digits. This value is referred to as the
offset. If the digits are 0xabcde
, then resource ids
are allocated in sequence 0xabcde001
, 0xabcde002
,
0xabcde003
... 0xabcdefff
, rather than the usual
0x00000001, 0x00000002, 0x00000003 ... 0x00000fff (decimal 4095).
a resource file signature, which must be the first resource in
the file, contains a reference to itself: this may be used by the
RResourceFile
class to identify the 20-bit offset for resource
ids, because the first resource id will be 0xabcde001
.
These ideas are explained below:
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
.
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.
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);
...