http://www.developer.com/

Back to article

Working with Resources in Palm OS Applications


January 17, 2005

Resource Types

Before diving into the Palm OS resource managemnent stuff, take a look at what you are going to manage. The following table describes the main resource types available in Palm OS:

Constant Value Description
strRsc 'tSTR' String resource
ainRsc 'tAIN' Application Icon Name resource
iconType 'tAIB' Icon resource
bitmapRsc 'Tbmp' Bitmap resource
bsBitmapRsc 'Tbsb' Boot screen bitmap. This resource is system reserved
alertRscType 'Talt' Alert resource
kbdRscType 'tkbd' Keyboard resource. This resource is system reserved
MenuRscType 'MBAR' Menu Bar resource
fontRscType 'NFNT' Single density Font resource
fontExtRscType 'nfnt' High density Font resource
verRsc 'tver' Version resource
appInfoStringsRsc 'tAIS' Application Categories are store here. This resource can be passed to the CategoryInitialize() API
fontIndexType 'fnti' Represents the font index. This resource is system reserved
midiRsc 'MIDI' Palm MIDI compliant resource
colorTableRsc 'tclt' UI Color Table resource
constantRscType 'tint' Integer constant resource. ResLoadConstant() API can be called to load the this type of resource
formRscType 'tFRM' UI Form resource
strListRscType 'tSTL' This is a string list resource
wrdListRscType 'wrdl' List of WORD (2 bytes) values
defaultCategoryRscType 'taic' Launcher Category ID. Your application will be placed under the appropriate category on the Palm device
binaryGeneralRscType 'tbin' General binary resource. You may store any raw data here

After such a smart start, you can see how rich this game is. It's odd to say that, by using resources, you make all localization issues much easier. For custom launch codes, when you can't have global variables, a resource may be an ideal solution to work around it, and so forth. If you're already convinced, the next sections will guide you through the supporting API calls.

Searching Resource Databases

First of all, consider how to conduct a resources search. DataMgr.h defines the following functions for this purpose:

DmOpenRef  DmNextOpenResDatabase(DmOpenRef dbP);
UInt16     DmFindResourceType(DmOpenRef dbP, DmResType resType,
                              UInt16 typeIndex);
UInt16     DmFindResource(DmOpenRef dbP, DmResType resType,
                          DmResID resID, MemHandle resH);
UInt16     DmSearchResource(DmResType resType, DmResID resID,
                            MemHandle resH, DmOpenRef *dbPP);

These functions expose various ways to get the desired results. Usually, resources are stored in PDB on a Palm device. When you request a specific resource, the Palm OS will start from recently opened databases. The DmNextOpenResDatabase function controls which database will be searched for. The first one in the search chain will be returned, passing NULL as a parameter. Actually, it is the last loaded resource database; for example, your application. Note, that in case of using overlays, the returned pointer will reference the overlay, not the base resource.

DmFindResource and DmFindResourceType run over a given database for specified resource parameters such as type, index, ID, or memory handle. In turn, DmSearchResource does its job over all open resource databases and returns a reference to the appropriate database.

Although Palm OS 6 still supports a "search chain" notion for backward compatibility, new applications should not rely on this. To illustrate what was said above, take a look at the following code sample:

...
DmOpenRef dbP  = DmNextOpenResDatabase(NULL);
UInt16 nIdx    = DmFindResourceType(dbP,'tSTR',1);
MemHandle hMem = DmGetResourceIndex(dbP,nIdx);
Char *pData    = (Char*)MemHandleLock(hMem);
...
MemHandleUnlock(hMem);

This code snippet obtains a reference of the recent resource database (application itself in this case), searches for the string of index 1, and finally loads it by using a DmGetResourceIndex call. Here's a standard trick: First, obtain a resource index inside a database and then get it itself. Such a technique is useful when you need to enumerate some kind of resources and display them; for example, in some list.

Getting Access to Resources

The Data Manager exposes several functions to access resources stored in database:

MemHandle  DmGetResource(DmResType type, DmResID resID);
MemHandle  DmGet1Resource(DmResType type, DmResID resID);
Err        DmReleaseResource(MemHandle resourceH);
MemHandle  DmResizeResource(MemHandle resourceH, UInt32 newSize);
MemHandle  DmGetResourceIndex(DmOpenRef dbP, UInt16 index);

An application gets the resources by calling either DmGetResource, DmGet1Resource, or DmGetResourceIndex. The difference between the first two APIs is that DmGetResource searches all opened databases, whereas DmGet1Resource uses the most recently opened one. As an example, the following code loads a string list from the resource and fills in some other list:

// define global containers somewhere
char **pszTestList = NULL;
UInt16 g_nNumItems = 0;
...
   DmOpenRef dbP   = DmNextOpenResDatabase(NULL);
   MemHandle hSTL   = DmGet1Resource('tSTL',StrList);

   if ( hSTL )
   {
      Char *pSTL = (Char*)MemHandleLock(hSTL);
      pSTL += StrLen(pSTL)+1;
      MemMove(&g_nNumItems,pSTL,2);
      pSTL += 2;

      pszTestList = new Char*[g_nNumItems];
      for (UInt16 wIdx = 0; wIdx < g_nNumItems; wIdx++)
      {
         pszTestList[wIdx] = new Char[StrLen(pSTL) + 1];
         StrCopy(pszTestList[wIdx],pSTL);
         pSTL += StrLen(pSTL) + 1;
      }
      UInt16 lstIndex = FrmGetObjectIndex(frmP, ResViewTestList);
      ListType *listP = (ListType *)FrmGetObjectPtr(frmP, lstIndex);
      LstSetListChoices (listP,pszTestList,g_nNumItems);
      LstDrawList(listP);

      MemHandleUnlock(hSTL);
      DmReleaseResource(hSTL);
   }
...

// and then release all allocated data at exit
static void AppStop(void)
{

   /* Close all the open forms. */
   FrmCloseAllForms();

   for (UInt16 wIdx = 0; wIdx < g_nNumItems; wIdx++)
   {
      delete [] pszTestList[wIdx];
   }

   delete [] pszTestList;
}

This sample also gives you the internal structure of the 'tSTL' resource:

  • Prefix string
  • Number of items in list (2 bytes)
  • Strings themselves

So, by enumerating strings one by one, you can get them all. If you need to extract a specific string by a known index, you have another function to be called:

Char *SysStringByIndex (UInt16 resID, UInt16 index, Char *strP,
                        UInt16 maxLen);

// So it is...
// the very first string is taken
Char Str[255];
if ( SysStringByIndex(StrList, 0, Str, sizeof(Str)) )
{
...
}

The returned string is concatenated from the prefix and string at the given index.

The next snippet gives you one more simple example of how to retrieve raw data from a resource via a DmGetResource call:

DmOpenRef dbP = DmNextOpenResDatabase(NULL);

MemHandle hData = DmGetResource('tbin',HexSampleRes);
Char *pHexData  = (Char*)MemHandleLock(hData);
Int32 dwData;
MemMove(&dwData,pHexData,4);
MemHandleUnlock(hData);
DmReleaseResource(hData);

DmGetResource returns the resource located in the overlay if any open overlay has a resource matching that type and ID. Otherwise, this function continues to search the resource in the base database.

Finally, when the resource is no longer needed, you should call DmReleaseResource to release it. Obviously enough, the application must do it as soon as it does not need this resource any more to ease the OS's life.

The Last Piece

In this final section, you will explore a few more API calls. This kind of function handles resource creation and deletion, together with updating existing data. These functions are:

// Resource Info
UInt16  DmNumResources(DmOpenRef dbP);
Err     DmResourceInfo(DmOpenRef dbP, UInt16 index,
                       DmResType *resTypeP, DmResID *resIDP,
                       LocalID *chunkLocalIDP);
Err     DmSetResourceInfo(DmOpenRef dbP, UInt16 index,
                          DmResType *resTypeP, DmResID *resIDP);

// Resource attaching and detaching
Err  DmAttachResource(DmOpenRef dbP, MemHandle newH,
                      DmResType resType, DmResID resID);
Err  DmDetachResource(DmOpenRef dbP, UInt16 index, MemHandle *oldHP);

// Resource creation and deletion
MemHandle  DmNewResource(DmOpenRef dbP, DmResType resType,
                         DmResID resID, UInt32 size);
Err        DmRemoveResource(DmOpenRef dbP, UInt16 index);

As you can see, they are divided into three groups. The first group is informational and simple. The other two groups allow you to manage resources. DmAttachResource converts given memory chunk into a specified resource, so that's the way to create new resources on-the-fly. DmDetachResource removes a resource from the database and reassigns it to a memory chunk. All the rest is self-explained. I will not touch them here in details, leaving it for your own fun. That's it; you're ready to explore new resource islands.

About the Author

Alex Gusev started to play with mainframes at the end of the 1980s, using Pascal and REXX, but soon switched to C/C++ and Java on different platforms. When mobile PDAs seriously rose their heads in the IT market, Alex did it too. Now, he works at an international retail software company as a team leader of the Mobile R department, making programmers' lives in the mobile jungles a little bit simpler.

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date