Symbian
Symbian Developer Library

SYMBIAN OS V9.4

Feedback

[Index] [Previous] [Next]


How to convert data from a simple resource

An application often needs to convert the individual items within a resource into data which can be stored within the data members of a class.

A resource is defined in terms of a struct; this struct might be defined in terms of other structs, or arrays of structs.

The following code fragments show how a simple resource can be used to initialise an instance C++ class. The TResourceReader class provides a safe way to interpret individual items within the resource.

[Top]


Defining a simple resource

The following code fragment defines a simple struct. The convention is to define such structs in resource header files that, by convention, have the .rh extension. Resource header files are included in resource definition files, that have a .rss extension. This is analogous to the conventions for C++ header and source files.

// for defining a single data resource
STRUCT DATA
    {
    WORD      wrd=16;
    WORD      flags=0;
    LONG      lng;
    BYTE      byt; 
    DOUBLE    dbl=0.0;
    LTEXT     ltxt;        // variable length
    }

The WORD item labelled flags is used to contain flag bit settings. Each bit setting is defined by a corresponding symbol. Such symbols are almost always needed both by the resource and the C++ code, and are placed in a file with a .hrh extension. The .hrh file is included in both the resource files and the C++ files at the appropriate locations.

So, in the .hrh file, we might have:

                // define some flags
#define EFlagItem1  0x0001
#define EFlagItem2  0x0002
#define EFlagItem3  0x0004
...

The resource itself is defined within the resource file, the .rss file, and must include the resource header, the .rh file, so that the resource compiler can find the definition of the DATA struct. In this example, the resource header file is called ReadData.rh.

#include "ReadData.rh"

RESOURCE DATA first
    {
    wrd=0;
    flags=EFlagItem1+EFlagItem2;
    lng=2; 
    byt=255;
    dbl=99.9;
    ltxt=This is LTEXT type (leading byte count);
    }

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

#define FIRST 1

[Top]


Converting from the array resource

The following code fragment defines an example class, CResData, that uses the resource data as a construction parameter. The class definition is placed in its own C++ header file, a .h file.

class CResData : public CBase
    {
public:
    ~CResData()
    static    CResData* NewLC(TResourceReader& aReader);
    void      ShowData(const TInt aStructNum = 0);
private:
    void      ConstructL(TResourceReader& aReader);
private:
    TInt           iWrd;   // STRUCT member type: WORD,
    TInt           iFlags; // WORD
    TInt           iLng;   // LONG,
    TInt           iByt;   // BYTE,
    TReal          iDbl;   // DOUBLE,
    HBufC*         iLtxt;  // LTEXT
    };

The data members of the CResData class correspond to the individual data items within the resource as defined by the DATA struct.

The following example code fragment loads the resource, with id FIRST, into a descriptor and constructs the CResData object from that data using a TResourceReader object to interpret the component items:

    // Read the first resource
HBufC8* res = resourceFile.AllocReadLC(FIRST);
TResourceReader theReader;
theReader.setBuffer(res);
    // construct a CResData object to contain
    // the simple resource of type DATA.
CResData* resData = CResData::NewLC(theReader);

CResData::NewLC() allocates the CResData object and calls ConstructL() to complete the construction process.

ConstructL() takes a reference to a TResourceReader that refers to the resource data itself.

Note that raw resource data is always treated as general binary data.

The construction process of a CResData object, implemented by the ConstructL() function, extracts the individual members from the resource and stores them within the corresponding data members of the C++ class. Thus, the wrd member, whose type is WORD, is copied to iWrd and the flags member, whose type is also WORD, is copied to iFlags and so on.

void CResData::ConstructL(TResourceReader& aReader)
    {
                // Interpret next bytes as a TInt16
    iWrd   = aReader.ReadInt16();
                // Interpret next bytes as a TInt16
    iFlags = aReader.ReadInt16();
                // Interpret next bytes as a TInt32
    iLng   = aReader.ReadInt32();
                // Interpret the next byte as a TInt8
    iByt   = aReader.ReadInt8();
                // Interpret next bytes as a TReal
    iDbl   = aReader.ReadReal64();
                // Interpret the next byte as the length
                // of text. The text itself follows
                // this byte.
    iLtxt  = aReader.ReadHBufCL();
    }

This code uses the TResourceReader object to interpret and extract the component items of the resource data. The extracted component items are copied into the members of the CResData class.

The resource data is expected to be described by the DATA struct. It is important to note that if the DATA struct were altered, then this code would also need to be altered. Supplying a resource that does not follow the pattern described by the DATA struct, would not prevent the C++ code from compiling successfully. However, the results of execution would be unpredictable. Thus, the resource loading system is not type safe in the same way that C++ programming is.

The integers in the C++ class are specified as TInt values. This means that they are the natural word size of the machine, at least 32 bits. The corresponding resource data, however, is specified as either BYTE, WORD or LONG members: 8, 16 or 32 bits. The data is broadened as it is read into the C++ variables. As a rule, resource data should be stored in as compact a form as possible, taking into account the variation in possible values.

See also

Integral types