Important: The information in this document is obsolete and should not be used for new development.
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
UseResFileprocedure. (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 asGet1Resource,Get1NamedResource,Get1IndResource,Unique1ID, andCount1Resources.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
SetResLoadprocedure 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 callingSetResLoadwith theloadparameter set toTRUE. If you callSetResLoadwith theloadparameter set toFALSE, 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 toNILunless 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 toSetResLoadwith theloadparameter set toFALSE, callLoadResource.
In addition to the
- WARNING
- If you call
SetResLoadwith theloadparameter set toFALSE, be sure to callSetResLoadwith theloadparameter set toTRUEas soon as possible. Other parts of system software that call the Resource Manager rely on the default setting (theloadparameter set toTRUE), and some routines won't work if resources are not loaded automatically.![]()
SetResLoadprocedure, 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.
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
- 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.
![]()
kLastWinStateIDto 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 typeMyWindowState:
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 calledMySetWindowPositionthat 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 usesMySetWindowPositionto 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;TheMySetWindowPositionprocedure uses theFSpOpenResFilefunction to open the document's resource fork, then usesGet1Resourceto 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
Handledata type. You usually define a data type (such asMyWindowState) to access the resource's data. If you also define a handle to your defined data type (such asMyWindowStateHnd), 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 typeHandlewhen you use other Resource Manager routines. For example, after it has finished moving the window,MySetWindowPositionusesReleaseResourceto 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 toNIL). 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,MySetWindowPositionuses theCloseResFileprocedure to close the resource fork.
The Resource Manager also provides routines that let you index through all resources of a given type (for example, using
- Note
- Listing 1-9 assumes the window state resource is not purgeable. If it were,
MySetWindowPositionwould need to callLoadResourcebefore accessing the data in the resource.![]()
CountResourcesandGetIndResource). 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'), theMyDoOpenSoundResourcesprocedure callsFSpOpenResFileto open the file's resource fork and obtain its file reference number. (IfFSpOpenResFilefails to open the resource fork, it returns -1 instead of a file reference number.) TheMyDoOpenSoundResourcesprocedure then uses theCount1Resourcesfunction to count the number of'snd 'resources in the resource fork. It can then index through the resources one at a time, usingGet1IndResourceto open each resource,GetResInfoto get the resource's name, andAppendMenuto append each name to SurfWriter's Sounds menu.
- Note
- In most situations, you can use the Menu Manager procedure
AppendResMenuto add names of resources to a menu. See Inside Macintosh: Macintosh Toolbox Essentials for details.![]()