Legacy Documentclose button

Important: The information in this document is obsolete and should not be used for new development.

Previous Book Contents Book Index Next

Inside Macintosh: More Macintosh Toolbox /
Chapter 1 - Resource Manager / Using the Resource Manager


Reading and Manipulating Resources

The Resource Manager provides a number of routines that read resources from a resource fork. When you request a resource, the Resource Manager follows the search path described in "Search Path for Resources" on page 1-8. That is, the Resource Manager searches each resource fork open to your application, beginning with the current resource file, and continues until it either finds the resource or reaches the end of the chain.

You can change where the Resource Manager starts its search using the UseResFile procedure. (See the previous section, "Specifying the Current Resource File," for details.) You can limit the search to only the current resource file by using the Resource Manager routines that contain a "1" in their names, such as Get1Resource, Get1NamedResource, Get1IndResource, Unique1ID, and Count1Resources.

To get a resource, you can specify it by its resource type and resource ID or by its resource type and resource name. By convention, most applications refer to a resource by its resource type and resource ID, rather than by its resource type and resource name.

You can use the SetResLoad procedure to enable and disable automatic loading of resource data into memory for routines that return handles to resources. Such routines normally read the resource data into memory if it's not already there. This is the default setting and the effect of calling SetResLoad with the load parameter set to TRUE. If you call SetResLoad with the load parameter set to FALSE, subsequent calls to routines that return handles to resources will not load the resource data into memory. Instead, such routines return a handle whose master pointer is set to NIL unless the resource is already in memory. This setting is useful when you want to read from the resource map without reading the resource data into memory. To read the resource data into memory after a call to SetResLoad with the load parameter set to FALSE, call LoadResource.

WARNING
If you call SetResLoad with the load parameter set to FALSE, be sure to call SetResLoad with the load parameter set to TRUE as soon as possible. Other parts of system software that call the Resource Manager rely on the default setting (the load parameter set to TRUE), and some routines won't work if resources are not loaded automatically.
In addition to the SetResLoad procedure, you can use the preloaded attribute of an individual resource to control loading of that resource's data into memory. The Resource Manager loads a resource into memory when it first opens a resource fork if the resource's preloaded attribute is set.

Note
If both the preloaded attribute and the locked attribute are set, the Resource Manager loads the resource as low in the heap as possible.
Here's an example of a situation in which an application might need to read a resource. The SurfWriter application always saves the last position of a document window when the user saves the document, storing this information in a resource that it has defined for this purpose. SurfWriter defines a resource with resource type rWinState and resource ID kLastWinStateID to store information about the window (its position and its state--that is, either the user state or the standard state). SurfWriter's window state resource has this format, defined by a record of type MyWindowState:

TYPE MyWindowState =
   RECORD
      userStateRect: Rect;    {user state rectangle}
      zoomState:     Boolean; {window state: TRUE = standard; }
                              {              FALSE = user}
   END;
   MyWindowStatePtr = ^MyWindowState;
   MyWindowStateHnd = ^MyWindowStatePtr;
Listing 1-9 shows a procedure called MySetWindowPosition that the SurfWriter application uses in the process of opening a document. The SurfWriter application stores the last location of a document in its window state resource. When SurfWriter opens the document again, it uses MySetWindowPosition to read the document's window state resource and uses the resource data to set the window's location.

Listing 1-9 Getting a resource from a document file

PROCEDURE MySetWindowPosition (myWindow: WindowPtr);
VAR
   myData:              MyDocRecHnd;
   lastUserStateRect:   Rect;
   stdStateRect:        Rect;
   curStateRect:        Rect;
   myRefNum:            Integer;
   myStateHandle:       MyWindowStateHnd;
   resourceGood:        Boolean;
   savePort:            GrafPtr;
   myErr:               OSErr;
BEGIN
   myData := MyDocRecHnd(GetWRefCon(myWindow));    {get document record}
   HLock(Handle(myData));        {lock the record while manipulating it}
   {open the resource fork and get its file reference number}
   myRefNum := FSpOpenResFile(myData^^.fileFSSpec, fsRdWrPerm);
   myErr := ResError;
   IF myErr <> noErr THEN
      Exit(MySetWindowPosition);
   {get handle to rectangle that describes document's last window position}
   myStateHandle := MyWindowStateHnd(Get1Resource(rWinState,
                                                   kLastWinStateID));
   IF myStateHandle <> NIL THEN                 {handle to data succeeded}
   BEGIN    {retrieve the saved user state}
      lastUserStateRect := myStateHandle^^.userStateRect;
      resourceGood := TRUE;
   END
   ELSE
   BEGIN
      lastUserStateRect.top := 0;   {force MyVerifyPosition to calculate }
      resourceGood := FALSE;        { the default position}
   END;



   {verify that user state is practical and calculate new standard state}
   MyVerifyPosition(myWindow, lastUserStateRect, stdStateRect);
   IF resourceGood THEN                   {document had state resource}
      IF myStateHandle^^.zoomState THEN   {if window was in standard state }
         curStateRect := stdStateRect     { when saved, display it in }
                                          { newly calculated standard state}
      ELSE                 {otherwise, current state is the user state}
         curStateRect := lastUserStateRect
   ELSE                                   {document had no state resource}
      curStateRect := lastUserStateRect;  {use default user state}
   {move window}
   MoveWindow(myWindow, curStateRect.left, curStateRect.top, FALSE);
   {convert to local coordinates and resize window}
   GetPort(savePort);
   SetPort(myWindow);
   GlobalToLocal(curStateRect.topLeft);
   GlobalToLocal(curStateRect.botRight);
   SizeWindow(myWindow, curStateRect.right, curStateRect.bottom, TRUE);
   IF resourceGood THEN    {reset user state and standard }
   BEGIN                   { state--SizeWindow may have changed them}
      MySetWindowUserState(myWindow, lastUserStateRect);
      MySetWindowStdState(myWindow, stdStateRect);
   END;
   ReleaseResource(Handle(myStateHandle));         {clean up}
   CloseResFile(myRefNum);
   HUnlock(Handle(myData));
   SetPort(savePort);
END;
The MySetWindowPosition procedure uses the FSpOpenResFile function to open the document's resource fork, then uses Get1Resource to get a handle to the resource that contains information about the window's last position. The procedure can then verify that the saved position is practical and move the window to that position.

Note that when a Resource Manager routine returns a handle to a resource, the routine returns the resource using the Handle data type. You usually define a data type (such as MyWindowState) to access the resource's data. If you also define a handle to your defined data type (such as MyWindowStateHnd), you need to coerce the returned handle to the appropriate type, as shown in this line from Listing 1-9:

myStateHandle := MyWindowStateHnd(Get1Resource(rWinState, kLastWinStateID));
If you use this method, you also need to coerce your defined handle back to a handle of type Handle when you use other Resource Manager routines. For example, after it has finished moving the window, MySetWindowPosition uses ReleaseResource to release the memory allocated to the resource's data (which also sets the master pointer of the resource's handle in the resource map in memory to NIL). As shown in this line from Listing 1-9, SurfWriter coerces the defined handle back to a handle:

ReleaseResource(Handle(myStateHandle));
After releasing the resource data's memory, MySetWindowPosition uses the CloseResFile procedure to close the resource fork.

Note
Listing 1-9 assumes the window state resource is not purgeable. If it were, MySetWindowPosition would need to call LoadResource before accessing the data in the resource.
The Resource Manager also provides routines that let you index through all resources of a given type (for example, using CountResources and GetIndResource). This can be useful whenever you want to read all the resources of a given type.

Listing 1-10 shows an application-defined procedure that allows a user to open a file that contains sound resources. The SurfWriter application opens the specified file, counts the number of 'snd ' resources in the file, then performs an operation on each 'snd ' resource (adding the name of each resource to its Sounds menu).

Listing 1-10 Counting and indexing through resources

PROCEDURE MyDoOpenSoundResources;
VAR
   mySFReply:     StandardFileReply;{reply record}
   myNumTypes:    Integer;          {number of types to display}
   myTypeList:    SFTypeList;       {file type of files}
   myRefNum:      Integer;          {resource file reference no}
   mySndHandle:   Handle;           {handle to sound resource}
   numberOfSnds:  Integer;          {# of sounds in resource file}
   index:         Integer;          {index of sound resource}
   resName:       Str255;           {name of sound resource}
   curRes:        Integer;          {saved current resource file}
   myType:        ResType;          {resource type}
   myResID:       Integer;          {resource ID of snd resource}
   myWindow:      WindowPtr;        {window pointer}
   menu:          MenuHandle;       {handle to Sounds menu}
   myErr:         OSErr;            {error information}


BEGIN
   curRes := CurResFile;
   myWindow := FrontWindow;
   MyDoActivate(myWindow, FALSE);   {deactivate front window}
   myTypeList[0] := 'SFSD';         {show files of this type}
   myNumTypes := 1;  
   {let user choose a file that contains sound resources}
   StandardGetFile(NIL, myNumTypes, myTypeList, mySFReply);
   IF mySFReply.sfGood = TRUE THEN
   BEGIN
      myRefNum := FSpOpenResFile(mySFReply.sfFile, fsRdWrPerm);
      IF myRefNum = -1 THEN 
         DoError;
      menu := GetMenuHandle(mSounds);
      numberOfSnds := Count1Resources('snd ');  
      FOR index := 1 TO numberOfSnds DO
      BEGIN {the loop}
         mySndHandle := Get1IndResource('snd ', index);  
         IF mySndHandle = NIL THEN
            DoError
         ELSE
         BEGIN
            GetResInfo(mySndHandle, myResID, myType, resName);
            AppendMenu(menu, resName);
            ReleaseResource(mySndHandle);
         END;  {of mySndHandle <> NIL}
      END;  {of the loop}
   UseResFile(curRes);
   gSoundResFileRefNum := myRefNum;
   END;  {of sfReply.good}
END;
After the user selects a file that contains SurfWriter sound resources (that is, a file of type 'SFSD'), the MyDoOpenSoundResources procedure calls FSpOpenResFile to open the file's resource fork and obtain its file reference number. (If FSpOpenResFile fails to open the resource fork, it returns -1 instead of a file reference number.) The MyDoOpenSoundResources procedure then uses the Count1Resources function to count the number of 'snd ' resources in the resource fork. It can then index through the resources one at a time, using Get1IndResource to open each resource, GetResInfo to get the resource's name, and AppendMenu to append each name to SurfWriter's Sounds menu.

Note
In most situations, you can use the Menu Manager procedure AppendResMenu to add names of resources to a menu. See Inside Macintosh: Macintosh Toolbox Essentials for details.

Previous Book Contents Book Index Next

© Apple Computer, Inc.
6 JUL 1996