Important: The information in this document is obsolete and should not be used for new development.
Accessing a Resource-Based Profile With a Procedure
The ColorSync Manager provides for multiple concurrent accesses to a single profile through the use of a private data structure called a profile reference. When you call theCMOpenProfile
function to open a profile or theCMNewProfile
,CWNewLinkProfile
, orCMCopyProfile
functions to create or copy a profile, you pass a profile location and the function returns a profile reference. To specify the profile location, you use a structure of typeCMProfileLocation
, as described in "Opening a Profile and Obtaining a Reference to It" (page 4-17).A ColorSync profile that you open or create is typically stored in one of the following locations:
The sample code in Listing 4-12 to Listing 4-23 demonstrates how to use a profile access procedure to provide access to a resource-based profile.
- In a disk file. The
u
field (a union) of theCMProfileLocation
data structure contains a file specification for a profile that is disk-file based. This is the most common way to store a ColorSync profile.- In relocatable memory. The
u
field of the profile location data structure contains a handle specification for a profile that is stored in a handle.- In nonrelocatable memory. The
u
field of the profile location data structure contains a pointer specification for a profile that is pointer based.- In an arbitrary location accessed by a procedure you provide. The
u
field of the profile location data structure contains a universal procedure pointer to your access procedure, as well a pointer that may point to data associated with your procedure.
- Note
- While the following sample code includes some error handling, more complete error handling is left as an exercise for the reader.
Defining a Data Structure for a Resource-Based Profile
The sample code listings that follow use the application-defined MyResourceLocRec data structure. It stores information to describe a resource-based profile, including
- the resource file specification
- the resource type
- the resource ID
- the resource file reference
- the resource handle
- the profile access procedure pointer
- the resource name
struct MyResourceLocRec { FSSpec resFileSpec; ResType resType; short resID; short resFileRef; Handle resHandle; CMProfileAccessUPPproc; Str255 resName; }; typedef struct MyResourceLocRec MyResourceLocRec, *MyResourceLocPtr;The ColorSync Manager defines the CMProfileAccessUPP type as follows:
typedef UniversalProcPtr CMProfileAccessUPP;Setting Up a Location Structure for Procedure Access to a Resource-Based Profile
The MyCreateProcedureProfileAccess routine shown in Listing 4-12 sets up a CMProfileLocation structure for procedure access to a resource-based profile. The MyDisposeProcedureProfileAccess routine, shown in Listing 4-13, disposes of memory allocated by MyCreateProcedureProfileAccess. Your application uses these routines (or similar ones that you write) in the following way:
For the sample MyCreateProcedureProfileAccess routine shown in Listing 4-12, you pass a pointer to a CMProfileLocation structure to fill in, a pointer to a file specification for the resource file containing the profile resource, the type of the resource, the ID for the resource, and optionally the name of the resource (stored as a Pascal string, where the first byte is a length byte for the string).
- Before calling a ColorSync routine such as
CMCopyProfile
, you call the MyCreateProcedureProfileAccess routine to set up a CMProfileLocation structure that you can pass to the ColorSync routine. The location structure specifies your profile-access procedure and may provide other information as well. A sample profile-access procedure is shown in Listing 4-14.- During the course of its operations, ColorSync may call your profile-access procedure many times.
- After the ColorSync routine has completed its operation, and if your application does not need to use the CMProfileLocation structure for another operation, you call the MyDisposeProcedureProfileAccess routine to dispose of memory allocated by MyCreateProcedureProfileAccess.
Listing 4-12 Setting up a location structure for procedure access to a resource-based profile
- Note
- Listing 4-12 assumes the profile access routine, MyCMProfileAccessProc, is within the scope of the MyCreateProcedureProfileAccess routine. Optionally, you could add a parameter to pass in a procedure pointer for the profile access routine.
OSErr MyCreateProcedureProfileAccess ( CMProfileLocation *profileLocation, FSSpec *resourceSpec, Str255 resourceName, OSType resourceType, short resourceID) { OSErr err = noErr; MyResourceLocPtrresourceInfo; /* allocate memory for our private resource info structure */ resourceInfo = (MyResourceLocPtr) NewPtrClear(sizeof(MyResourceLocRec)); if (!resourceInfo) err = MemError(); if (!err) { /* set up our private resource info structure */ resourceInfo->resFileSpec = *resourceSpec; resourceInfo->resType = resourceType; resourceInfo->resID = resourceID; resourceInfo->resFileRef = 0; resourceInfo->resHandle = 0; resourceInfo->proc = NewCMProfileAccessProc(MyCMProfileAccessProc); /* if a resource name was passed in, copy it to the structure; since it's a Pascal string, first byte is length; note that BlockMoveData is faster than BlockMove for a move that involves data only */ if (resourceName) BlockMoveData(resourceName, resourceInfo->resName, resourceName[0]); /* set up the profile location structure */ profileLocation->locType = cmProcedureBasedProfile; profileLocation->u.procLoc.refCon = (void*) resourceInfo; profileLocation->u.procLoc.proc = resourceInfo->proc; } return err; }If the MyCreateProcedureProfileAccess routine is able to set up the profile location pointer for procedure access to a resource-based profile, it returns a value ofnoErr
.Disposing of a Resource-Based Profile Access Structure
Your application calls the MyDisposeProcedureProfileAccess routine, shown in Listing 4-13, to dispose of any memory allocated by the MyCreateProcedureProfileAccess routine, shown in Listing 4-12.Listing 4-13 Disposing of a resource-based profile access structure
void MyDisposeProcedureProfileAccess (CMProfileLocation *profileLocation) { DisposeRoutineDescriptor(profileLocation->u.procLoc.proc); /* dispose of our private resource info structure */ DisposePtr((Ptr)profileLocation->u.procLoc.refCon); }This routine first disposes of the universal procedure pointer to your profile access procedure, then disposes of the pointer used to store resource data in a MyResourceLocRec structure.Responding to a Procedure-Based Profile Command
For the procedure declaration for a profile access procedure, see MyCMProfileAccessProc (page 3-172) in Advanced Color Imaging Reference. The ColorSync Manager calls your procedure when the profile is created, initialized, opened, read, updated, or closed, passing a command constant that specifies the current command. Your profile access procedure must be able to respond to each of the following command constants (described in "Profile Access Procedure Operation Codes" in Advanced Color Imaging Reference):
enum { cmOpenReadAccess= 1, cmOpenWriteAccess= 2, cmReadAccess = 3, cmWriteAccess = 4, cmCloseAccess = 5, cmCreateNewAccess= 6, cmAbortWriteAccess= 7, cmBeginAccess = 8, cmEndAccess = 9 };The profile access procedure shown in Listing 4-14,MyCMProfileAccessProc
, consists of a single switch statement, which calls the appropriate routine based on the value of thecommand
parameter. Each of the nine routines called byMyCMProfileAccessProc
is described and listed in the sections that follow Listing 4-14, and each refers back to Listing 4-14.Listing 4-14 Responding to a procedure-based profile command
pascal OSErr MyCMProfileAccessProc (long command, long offset, long *sizePtr, void *dataPtr, void *refConPtr) { OSErr err = noErr; switch (command) { case cmBeginAccess: err = DoBeginAccess(refConPtr); break; case cmCreateNewAccess: err = DoCreateNewAccess(refConPtr); break; case cmOpenReadAccess: err = DoOpenReadAccess(refConPtr); break; case cmOpenWriteAccess: err = DoOpenWriteAccess(sizePtr, refConPtr); break; case cmReadAccess: err = DoReadAccess(offset, sizePtr, dataPtr, refConPtr); break; case cmWriteAccess: err = DoWriteAccess(offset, sizePtr, dataPtr, refConPtr); break; case cmCloseAccess: err = DoCloseAccess(refConPtr); break; case cmAbortWriteAccess: err = DoAbortWriteAccess(refConPtr); break; case cmEndAccess: err = DoEndAccess(refConPtr); break; default: err = paramErr; break; } return err; }The MyCMProfileAccessProc routine passes its parameter data as necessary to the routines it calls. The parameters have the following values:
- command
- A command value indicating the operation to perform. The possible values for command constants are shown elsewhere in this section.
- offset
- For read and write operations, the offset from the beginning of the profile at which to read or write data.
- size
- For the cmReadAccess and cmWriteAccess command constants, a pointer to a value indicating the number of bytes to read or write; for the cmOpenWriteAccess command, the total size of the profile. On output after reading or writing, the actual number of bytes read or written.
- data
- A pointer to a buffer containing data to read or write. On output, for a read operation, contains the data that was read.
- refConPtr
- A reference constant pointer that can store private data for the MyCMProfileAccessProc procedure. For example, Listing 4-12 shows how to set up a location structure for procedure access to a resource-based profile. That routine sets the location structure's refCon field to a pointer to a MyResourceLocRec structure (page 4-58). That same structure pointer is passed to the MyCMProfileAccessProc routine in the refConPtr parameter and provides access to all the stored information about the resource location.
Handling the Begin Access Command
When the ColorSync Manager needs to signal that it is time to prepare for a session of reading, writing, or both for a procedure-based profile, it invokes the specified profile access procedure with the cmBeginAccess command. This happens, for example, when your application calls theCMOpenProfile
routine, specifying as a location a procedure-based profile.When your profile-access procedure is called with the cmBeginAccess command, it performs any required initialization or validation tasks, such as determining whether the data pointed to by the
refcon
parameter is valid. If your procedure returns an error (any value exceptnoErr
), the ColorSync Manager will not call your profile access procedure again.For the cmBeginAccess command, the sample profile access procedure shown in Listing 4-14 calls the DoBeginAccess routine, shown in Listing 4-15. DoBeginAccess interprets the
refcon
parameter as a MyResourceLocPtr type. If the parameter does not have a resource type of kProcResourceType, DoBeginAccess returns an invalid profile error, which effectively cancels the procedure-based profile access.Listing 4-15 Handling the begin access command
static OSErr DoBeginAccess (void *refcon) { OSErr err; MyResourceLocPtrresourceInfo = refcon; resourceInfo->resFileRef = 0; if (resourceInfo->resType != kProcResourceType) err = cmInvalidProfileLocation; else err = noErr; return err; }Handling the Create New Access Command
When the ColorSync Manager needs to signal that it is time to allocate any required new resources for a procedure-based profile, it invokes the specified profile access procedure with the cmBeginAccess command, as described in "Handling the Begin Access Command" (page 4-64). This happens, for example, when your application calls theCMCopyProfile
routine, specifying as a location a procedure-based profile.If your profile access procedure returns without error, ColorSync calls the procedure again with the cmCreateNewAccess command. Your procedure then creates a new data stream for the actual physical location of the profile. The size of the profile is not known at this point.
For the cmCreateNewAccess command, the sample profile access procedure shown in Listing 4-14 calls the DoCreateNewAccess routine. DoCreateNewAccess interprets the
refcon
parameter as a MyResourceLocPtr type and calls the Toolbox routine FSpCreateResFile to create an empty resource fork based on the file specification provided by the MyResourceLocPtr type. If the resource fork does not already exist and cannot be created, DoCreateNewAccess returns an error.For this example, the file type for a resource-based profile was chosen arbitrarily to be
'rprf'
.Listing 4-16 Handling the create new access command
OSErr DoCreateNewAccess (void *refcon) { OSErr err; MyResourceLocPtrresourceInfo = refcon; FSpCreateResFile(&(resourceInfo->resFileSpec), '????', 'rprf', 0); err = ResError(); if (err == dupFNErr) err = noErr; return err; }Handling the Open Read Access Command
When your application calls a ColorSync routine to read information from a procedure-based profile, the ColorSync Manager first calls your profile access procedure with the cmOpenReadAccess command. ColorSync calls your profile access routine once for each read session. The sample profile access procedure shown in Listing 4-14 calls the DoOpenReadAccess routine.The DoOpenReadAccess routine shown in Listing 4-17 uses information from the refcon parameter, interpreted as type MyResourceLocPtr, to open the resource fork for the resource-based profile with read permission. If it can open the resource file, DoOpenReadAccess then attempts to load the profile resource.
The DoOpenReadAccess routine shows good citizenship by saving the current resource file before performing its operations and restoring the resource file afterward.
Listing 4-17 Handling the open read access command
static OSErr DoOpenReadAccess (void *refcon) { OSErr err; MyResourceLocPtrresourceInfo = refcon; short currentResFile; /* save current resource file */ currentResFile = CurResFile(); /* open the file's resource fork */ resourceInfo->resFileRef = FSpOpenResFile(&(resourceInfo->resFileSpec), fsRdPerm); err = ResError(); /* get the resource handle, but don't force it to be loaded into memory */ if (!err) { SetResLoad(false); resourceInfo->resHandle = GetResource(resourceInfo->resType, resourceInfo->resID); err = ResError(); SetResLoad(true); } /* restore previous resource file */ UseResFile(currentResFile); return err; }Handling the Open Write Access Command
When the ColorSync Manager needs to signal that a procedure-based profile should be opened for writing, it invokes the specified profile access procedure with the cmOpenWriteAccess command. This happens, for example, when your application calls theCMUpdateProfile
routine to update a procedure-based profile. The sample profile access procedure shown in Listing 4-14 calls the DoOpenWriteAccess routine.The DoOpenWriteAccess routine shown in Listing 4-18 uses information from the refcon parameter, interpreted as type MyResourceLocPtr, to open the resource fork for the resource-based profile with read/write permission. If it can open the resource file, DoOpenWriteAccess then attempts to open the specified profile resource. If it can't open the resource, DoOpenWriteAccess creates a new resource. It then sets the size of the resource based on the passed setProfileSize pointer value and updates the resource file.
The DoOpenWriteAccess routine shows good citizenship by saving the current resource file before performing its operations and restoring the resource file afterward.
Listing 4-18 Handling the open write access command
- Note
- If the cmOpenWriteAccess command succeeds, ColorSync guarantees an eventual call to the profile access procedure with the cmCloseAccess command, possibly after multiple cmWriteAccess commands, and possibly after a cmAbortWriteAccess command.
static OSErr DoOpenWriteAccess (long *setProfileSize, void *refcon) { OSErr err; MyResourceLocPtrresourceInfo = refcon; Size resourceSize; short currentResFile; /* save current resource file */ currentResFile = CurResFile(); /* open the file's resource fork */ resourceInfo->resFileRef = FSpOpenResFile(&(resourceInfo->resFileSpec), fsRdWrPerm); err = ResError(); /* get the resource handle, but don't force it to be loaded into memory */ if (!err) { SetResLoad(false); resourceInfo->resHandle = GetResource(resourceInfo->resType, resourceInfo->resID); err = ResError(); SetResLoad(true); } /* call GetResourceSizeOnDisk to see if resource is already there */ if (!err) { /* get size of the resource */ resourceSize = GetResourceSizeOnDisk(resourceInfo->resHandle); err = ResError(); } /* if the above call to GetResourceSizeOnDisk returns resNotFound, */ /* then we need to create a new resource */ if (err == resNotFound) { /* allocate a temporary handle just so that we can call AddResource */ resourceInfo->resHandle = NewHandle(sizeof(long)); err = MemError(); /* add resource to the file and release the temp handle */ if (!err) { AddResource(resourceInfo->resHandle, resourceInfo->resType, resourceInfo->resID, resourceInfo->resName); err = ResError(); ReleaseResource(resourceInfo->resHandle); } /* get the resource handle, but don't force it to be loaded into memory */ if (!err) { SetResLoad(false); resourceInfo->resHandle = GetResource(resourceInfo->resType, resourceInfo->resID); err = ResError(); SetResLoad(true); } } /* change the resource size to fit the profile */ if (!err) { SetResourceSize(resourceInfo->resHandle, *setProfileSize); err = ResError(); } /* force an update of the resource file */ if (!err) { UpdateResFile(resourceInfo->resFileRef); err = ResError(); } /* restore previous resource file */ UseResFile(currentResFile); return err; }Handling the Read Access Command
When your application calls a ColorSync routine to read information from a procedure-based profile, the ColorSync Manager first calls your profile access procedure with the cmOpenReadAccess command, as described in "Handling the Open Read Access Command" (page 4-66). Your profile access routine can be called with the cmReadAccess command at any time after the cmOpenReadAccess command is called. When the sample profile access procedure shown in Listing 4-14 receives the cmReadAccess command, it calls the DoReadAccess routine.The DoReadAccess routine shown in Listing 4-19 uses the refcon parameter, interpreted as type MyResourceLocPtr, to get a resource handle for the resource-based profile. From other parameters, it gets values for the offset at which to start reading, the number of bytes to read, and a pointer to a buffer in which to store the data that it reads. It then calls the Toolbox routine ReadPartialResource to do the actual reading.
If an error occurs while reading, DoReadAccess returns the error.
Listing 4-19 Handling the read access command
static OSErr DoReadAccess ( long offset, long *sizePtr, void *dataPtr, void *refcon) { OSErr err; MyResourceLocPtrresourceInfo = refcon; ReadPartialResource(resourceInfo->resHandle, offset, dataPtr, *sizePtr); err = ResError(); return err; }Handling the Write Access Command
When your application calls a ColorSync routine that writes information to a procedure-based profile, the ColorSync Manager first calls your profile access procedure with the cmOpenWriteAccess command. The DoOpenWriteAccess routine shown in Listing 4-18 performs certain operations to prepare to write a resource-based profile.Your profile access routine can be called with the cmWriteAccess command at any time after the cmOpenWriteAccess command is called. When the sample profile access procedure shown in Listing 4-14 receives the cmWriteAccess command, it calls the DoWriteAccess routine.
The DoWriteAccess routine shown in Listing 4-20 uses the refcon parameter, interpreted as type MyResourceLocPtr, to get a resource handle for the resource-based profile. From other parameters, it gets values for the offset at which to start writing, the number of bytes to write, and a pointer to a buffer from which to get the data that it writes. It then calls the Toolbox routine WritePartialResource to do the actual writing.
If an error occurs while writing, DoWriteAccess returns the error.
Listing 4-20 Handling the write access command
- Note
- After ColorSync calls the profile access procedure with the cmWriteAccess command, ColorSync is guaranteed to eventually call the profile access procedure with the cmCloseAccess command--possibly after additional calls with the cmWriteAccess command, and possibly after a call with the cmAbortWriteAccess command.
static OSErr DoWriteAccess (long offset, long *sizePtr, void *dataPtr, void *refcon) { OSErr err; MyResourceLocPtrresourceInfo = refcon; WritePartialResource(resourceInfo->resHandle, offset, dataPtr, *sizePtr); err = ResError(); return err; }Handling the Close Access Command
The ColorSync Manager calls your profile access procedure with the cmCloseAccess command to indicate that reading or writing is finished for the moment. A cmCloseAccess command can be followed by a cmOpenReadAccess command to begin reading again, a cmOpenWriteAccess command to begin writing again, or a cmEndAccess command to terminate the procedure-based profile access.The sample profile access procedure shown in Listing 4-14 calls the DoCloseAccess routine.
The DoCloseAccess routine shown in Listing 4-21 uses information from the refcon parameter, interpreted as type MyResourceLocPtr, to close and update the resource file for the resource-based profile. If DoCloseAccess is unsuccessful, it returns an error value.
Listing 4-21 Handling the close access command
static OSErr DoCloseAccess (void *refcon) { OSErr err; MyResourceLocPtrresourceInfo = refcon; /* close and update resource file */ if (resourceInfo->resFileRef) { CloseResFile(resourceInfo->resFileRef); err = ResError(); resourceInfo->resFileRef = 0; } else err = paramErr; return err; }Handling the Abort Write Access Command
If an error occurs between acmOpenWriteAccess
command and acmCloseAccess
command, the ColorSync Manager calls your profile access procedure with thecmAbortWriteAccess
command. This allows your access procedure to perform any cleanup necessary for the partially written profile.For the
cmAbortWriteAccess
command, the sample profile access procedure shown in Listing 4-14 calls the DoAbortWriteAccess routine.The DoAbortWriteAccess routine shown in Listing 4-22 uses information from the refcon parameter, interpreted as type MyResourceLocPtr, to call the Toolbox routine RemoveResource to delete the partially written resource. If DoAbortWriteAccess is unsuccessful, it returns an error value.
Listing 4-22 Handling the abort write access command
- Note
- The ColorSync Manager will call your profile access procedure with the
cmCloseAccess
command after acmAbortWriteAccess
command.
static OSErr DoAbortWriteAccess (void *refcon) { OSErr err; MyResourceLocPtrresourceInfo = refcon; /* delete the resource that we started */ if (resourceInfo->resHandle) { RemoveResource(resourceInfo->resHandle); err = ResError(); } else err = paramErr; return err; }Handling the End Access Command
When access to a procedure-based profile is complete, the ColorSync Manager calls your profile access procedure with thecmEndAccess
command. This allows your procedure to do any final cleanup, such as freeing memory allocated by the procedure.For the
cmEndAccess
command, the sample profile access procedure shown in Listing 4-14 calls the DoEndAccess routine. Because there is no additional memory to free or other cleanup to take care of, the DoEndAccess routine shown in Listing 4-23 does nothing.
Listing 4-23 Handling the end access command
- Note
- The MyCreateProcedureProfileAccess routine, shown in Listing 4-12, does allocate memory, which is freed by a call to the MyDisposeProcedureProfileAccess routine, shown in Listing 4-13. Your application calls the MyCreateProcedureProfileAccess routine before calling a ColorSync routine such as
CMCopyProfile
with a procedure-based profile. After the copy is complete, your application calls the MyDisposeProcedureProfileAccess routine to perform any necessary deallocation.
pascal OSErr DoEndAccess (void *refcon) { OSErr err = noErr; return err; }