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
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 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
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 callingSetResLoad
with theload
parameter set toTRUE
. If you callSetResLoad
with theload
parameter 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 toNIL
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 toSetResLoad
with theload
parameter set toFALSE
, callLoadResource
.
In addition to the
- WARNING
- If you call
SetResLoad
with theload
parameter set toFALSE
, be sure to callSetResLoad
with theload
parameter set toTRUE
as soon as possible. Other parts of system software that call the Resource Manager rely on the default setting (theload
parameter set toTRUE
), and some routines won't work if resources are not loaded automatically.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.
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.
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 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 calledMySetWindowPosition
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 usesMySetWindowPosition
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;TheMySetWindowPosition
procedure uses theFSpOpenResFile
function to open the document's resource fork, then usesGet1Resource
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 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 typeHandle
when you use other Resource Manager routines. For example, after it has finished moving the window,MySetWindowPosition
usesReleaseResource
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 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,MySetWindowPosition
uses theCloseResFile
procedure 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,
MySetWindowPosition
would need to callLoadResource
before accessing the data in the resource.CountResources
andGetIndResource
). 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'
), theMyDoOpenSoundResources
procedure callsFSpOpenResFile
to open the file's resource fork and obtain its file reference number. (IfFSpOpenResFile
fails to open the resource fork, it returns -1 instead of a file reference number.) TheMyDoOpenSoundResources
procedure then uses theCount1Resources
function to count the number of'snd '
resources in the resource fork. It can then index through the resources one at a time, usingGet1IndResource
to open each resource,GetResInfo
to get the resource's name, andAppendMenu
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.