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


Writing Resources

After opening a resource fork (as described in "Creating and Opening a Resource Fork" beginning on page 1-19), you can write resources to it. You can write resources only to the current resource file. To ensure that the current resource file is set to the appropriate resource fork, you can use CurResFile to save the file reference number of the
current resource file, then UseResFile to set the current resource file to the desired resource fork.

To specify data for a new resource, you usually use the AddResource procedure, which creates a new entry for the resource in the resource map in memory and sets the entry's location to refer to the resource's data. Note that AddResource changes only the resource map in memory; it doesn't change anything on disk. Use the UpdateResFile or WriteResource procedure to write the resource to disk. The AddResource procedure always adds the resource to the resource map in memory that corresponds to the current resource file. For this reason, you usually need to set the current resource file to the desired file before calling AddResource.

If you change a resource that is referenced through the resource map in memory, you use the ChangedResource procedure to set the resChanged attribute of that resource's entry. You should then immediately call the UpdateResFile or WriteResource procedure to write the changed resource data to disk. Note that although the UpdateResFile procedure writes only those resources that have been added or changed to disk, it also writes the entire resource map to disk (overwriting its previous contents). The WriteResource procedure writes only the resource data of a single resource to disk; it does not update the resource's entry in the resource map on disk.

The ChangedResource procedure reserves enough disk space to contain the changed resource. It does this every time it's called, but the actual writing of the resource does not take place until a call to WriteResource or UpdateResFile. Thus, if you call ChangedResource several times on a large resource before the resource is actually written, you may unexpectedly run out of disk space, because many times the amount of space actually needed is reserved. When the resource is actually written, the file's end-of-file (EOF) is set correctly, and the next call to ChangedResource will work as expected.

IMPORTANT
If your application frequently changes the contents of resources (especially large resources), you should call WriteResource or UpdateResFile immediately after calling ChangedResource.
To ensure that the Resource Manager does not purge a purgeable resource while your application is in the process of changing it, use the Memory Manager procedures HGetState, HNoPurge, and HSetState. First call HGetState and HNoPurge, then change the resource as necessary. To make a change to a resource permanent, use the ChangedResource and WriteResource (or UpdateResFile) procedures; then call HSetState when you're finished. (See Listing 1-2 on page 1-16 for an example of this technique.) However, most applications do not make resources purgeable and therefore don't need to take this precaution.

Here's an example of a situation in which an application might need to write a resource. As previously described, 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 the window state (its position and state, that is, either the user 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-11 shows SurfWriter's application-defined routine for saving the last position of a window in a window state resource in a document's resource fork.

Listing 1-11 Saving a resource to a resource fork

PROCEDURE MySaveWindowPosition (myWindow: WindowPtr;
                                myResFileRefNum: Integer);
VAR
   lastWindowState:  MyWindowState;
   myStateHandle:    MyWindowStateHnd;
   curResRefNum:     Integer;
BEGIN
   {set user state provisionally and determine whether window is zoomed}
   lastWindowState.userStateRect := WindowPeek(myWindow)^.contRgn^^.rgnBBox;
   lastWindowState.zoomState := EqualRect(lastWindowState.userStateRect,
                                           MyGetWindowStdState(myWindow));
   {if window is in standard state, then set the window's user state from }
   { the userStateRect field in the state data record}
   IF lastWindowState.zoomState THEN      {window was in standard state}
      lastWindowState.userStateRect := MyGetWindowUserState(myWindow);
   curResRefNum := CurResFile;   {save the refNum of current resource file}
   UseResFile(myResFileRefNum);  {set the current resource file}
   myStateHandle := MyWindowStateHnd(Get1Resource(rWinState,
                                                   kLastWinStateID));
   IF myStateHandle <> NIL THEN        {a state data resource already exists}
   BEGIN                               {update it}
      myStateHandle^^ := lastWindowState;
      ChangedResource(Handle(myStateHandle));
      IF ResError <> noErr THEN
         DoError;
   END
   ELSE                                {no state data has yet been saved}
   BEGIN                               {add state data resource}
      myStateHandle := MyWindowStateHnd(NewHandle(SizeOf(MyWindowState)));
      IF myStateHandle <> NIL THEN
      BEGIN
         myStateHandle^^ := lastWindowState;
         AddResource(Handle(myStateHandle), rWinState, kLastWinStateID,
                     'last window state');
      END;
   END;


   IF myStateHandle <> NIL THEN
   BEGIN
      UpdateResFile(myResFileRefNum);
      ReleaseResource(Handle(myStateHandle));
   END;
   UseResFile(curResRefNum);
END;
The MySaveWindowPosition procedure first sets the userStateRect field of the window state record to the bounds of the current content region of the window. It also sets the zoomState field of the record to a Boolean value that indicates whether the window is currently in the user state or standard state. If the window is in the standard state, the procedure sets the userStateRect field of the window state record to the user state of the window. (SurfWriter always saves the user state and the last state of the window. When it opens a document, it sets the user state to its previous state, verifies that this position is still valid, then calculates the window's standard state.)

The MySaveWindowPosition procedure then saves the file reference number of the current resource file and sets the current resource file to the document displayed in
the current window. The procedure then uses the Get1Resource function to determine whether the resource file of the document already contains a window state resource. If so, the procedure changes the resource data, then calls ChangedResource to set the resChanged attribute of the resource's entry of the resource map in memory. If the resource doesn't yet exist, the procedure simply adds the new resource using the AddResource procedure.

Note that when a Resource Manager routine returns a handle to a resource, it 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-11:

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, as shown in this line from Listing 1-11:

AddResource(Handle(myStateHandle), rWinState, kLastWinStateID,
            'last window state');
After MySaveWindowPosition changes or adds the resource (affecting only the resource map and resource data in memory), the MySaveWindowPosition procedure makes the change permanent by calling UpdateResFile and specifying the file reference number of the resource fork to update on disk. The UpdateResFile procedure writes the entire resource map in memory to disk and updates the resource data of any resource whose resChanged attribute is set in the resource map in memory. (If you want to update only the resource that was just changed or added, you can use WriteResource instead of UpdateResFile.)

Note
Listing 1-11 assumes the window state resource is not purgeable. If it were, MySaveWindowPosition would need to call HGetState and HNoPurge before changing the resource.
When done with the resource, MySaveWindowPosition uses ReleaseResource, which releases the memory allocated to the resource's data (and at the same time sets the master pointer of the resource's handle in the resource map in memory to NIL). Then MySaveWindowPosition restores the current resource file to its previous setting.


Previous Book Contents Book Index Next

© Apple Computer, Inc.
6 JUL 1996