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 2
Note 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
}
}