|
|
|
An application often needs to convert data from a resource defined as an array of structs. The example code here shows how this can be done.
The following code fragment defines a simple struct that forms an element of the resource array.
// 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 resource itself is defined in terms of an array, in this
example, the DATAARRAY struct. This is defined as:
// for defining an array of data
STRUCT DATAARRAY
{
STRUCT dataments[];
}The resource is composed of a number of DATA struct
elements.
The convention is to place such struct definitions 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.
The resource itself is defined within the resource file, the
.rss file, and must include the resource header file, the
.rh file, so that the resource compiler can find the definition of
the DATA and DATAARRAY structs. In this example, the
resource header file is called ReadArray.rh.
#include "ReadArray.rh"
RESOURCE DATA first
{
...
}
RESOURCE DATAARRAY second
{
dataments=
{
DATA
{
flags=EFlagItem1;
lng=654;
byt=-1;
ltxt=This text has a leading byte count;
},
DATA
{
wrd=999;
flags=EFlagItem1+EFlagItem2;
lng=3;
byt=255;
dbl=1.0;
ltxt=Extremely large text indeed abcd efghijklm;
},
DATA
{
wrd=0;
flags=EFlagItem16;
lng=-1;
byt=127;
dbl=12.34;
ltxt=;
},
DATA
{
wrd=-1;
flags=EFlagItem1+EFlagItem8+EFlagItem16;
lng=2147483647;
byt=128;
dbl=-3.4e+3;
ltxt={[@@@@@@@@@@@@@@]};
}
};
}After resource compilation, the generated .rsg header
file contains:
#define SECOND 2Note that in the example from which this is taken, this resource is the second resource definition.
The following code fragment defines an example
class, CResDataArray, that uses the resource data as a construction
parameter. The class definition is placed in a C++ header file,
a .h file. This header file also includes a definition of
the CResData class.
The CResData class is defined as:
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 CResDataArray class acts as a container for an array of
pointers to CResData objects, where each
CResData object corresponds to a DATA struct within
the DATAARRAY type resource. The CResDataArray class
is defined as:
class CResDataArray : public CBase
{
public:
~CResDataArray();
static CResDataArray* NewLC(TResourceReader& aReader);
void AddDataL(TResourceReader& aReader);
void ShowAllData();
private:
void ConstructL(TResourceReader& aReader);
private:
CArrayPtrFlat<CResData>* iDataArray;
};The following example code fragment loads the resource with
id SECOND and constructs the
CResDataArray object.
// Read the second resource
HBufC8* res = resourceFile.AllocReadLC(SECOND);
TResourceReader theReader;
theReader.SetBuffer(res);
// Construct a CResDataArray object to contain
// the array of CResData objects, and add the elements to it
CResDataArray* resDataArray = CResDataArray::NewLC(theReader);CResDataArray::NewLC() allocates
the CResDataArray object and calls ConstructL() to
complete the construction process.
ConstructL() takes a reference to
the TResourceReader that refers to the resource data itself. It
constructs the array object before starting the process of creating
the CResData elements using the AddDataL() member
function.
Note that raw resource data is always treated as general binary data.
void CResDataArray::ConstructL(TResourceReader& aReader)
{
iDataArray = new (ELeave) CArrayPtrFlat<CResData> (3);
TRAPD(error,AddDataL(aReader));
if (error)
{
iDataArray->ResetAndDestroy();
delete iDataArray;
User::Leave(error);
}
}AddDataL() interprets the first two bytes of the resource
data as the number of elements in the array.
On construction, each new CResData object is passed a
reference to the TResourceReader object.
The TResourceReader refers to that part of the resource data
corresponding to the start of a struct.
As each CResData object constructs its own data members from
the resource data using the services of the TResourceReader,
the TResourceReader object updates its internal pointers to the
resource data.
void CResDataArray::AddDataL(TResourceReader& aReader)
{
TInt index;
TInt number;
// The first WORD contains the number
// of DATA structs within the resource
number = aReader.ReadInt16();
// Add all newly created CResData objects
// to the cleanup stack before adding
// to the array
for (index = 0; index < number ; index++)
{
CResData* resData = CResData::NewLC(aReader);
iDataArray->AppendL(resData);
CleanupStack::Pop(); // now resData safely in array
}
}