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: Programmer's Guide to MacApp / Part 2 - Working With MacApp
Chapter 24 - Working With Memory and Failure Handling


Failure-Handling Overview

MacApp provides a failure-handling mechanism that allows your application to clean up after errors. This mechanism is described in detail in "Failure Handling," beginning on page 54. Your application can install failure handlers at multiple points. MacApp links the failure handlers together in a linked list, shown in Figure 3-2. You install a failure handler in the list with the Try macro, as shown in the following code fragment:

// Declare a failure handler.
FailInfo fi;

// Link the failure handler to the global list.
Try(fi)
{  // Code that may cause a failure.
   . . .

   // If no failure occurs, remove the failure handler from the list.
   fi.Success();
}
else
{  // Code to recover if an error occurs.
   .
   .
   .
   // In this case, pass error on to next handler.
   fi.ReSignal();
}
The Try block of code is executed first. Try is implemented as a macro rather than as a method of the FailInfo class, because the failure-handling code must be inline to properly save the current machine state. The Try macro links the failure handler to the list of failure handlers and calls setjmp to save the current machine state in the failure handler's buffer.

Note that the call to ReSignal, which invokes the next failure handler in the list, is not always necessary--the error code from the first handler may handle the error condition completely. ReSignal is a convenience routine that just calls Failure, passing the same message and error number values from the previous call. The Failure routine is described beginning on page 563.

Figure 24-2 shows the classes, methods, and routines used by MacApp and your application to handle failures and display error messages.

Figure 24-2 Failure-handling and error-message classes, methods, and routines

MacApp Error-Checking Routines

MacApp supplies the following global routines to check for common error conditions:

FailMemError
The FailMemError routine calls the Toolbox routine MemError to check whether the last memory operation caused an error. You call FailMemError after making a Toolbox call that affects memory, such as SetPtrSize or SetHandleSize.
FailNIL
The FailNIL routine tests whether the passed void * value is NULL. The most common value passed to FailNIL is a newly created object reference, pointer, or handle. FailNIL fails with memFullErr as the error message. This results in an error message of "Unable to <x> because there is not enough memory." <x> is a string that describes the current operation, such as "open a new document".
FailNILResource
The FailNILResource routine checks whether the handle returned by a Resource Manager call is NULL. You pass FailNILResource the handle returned by a Resource Manager call such as GetResource.
FailNonObject
The FailNonObject routine attempts to determine whether the passed pointer references an object, and generates a failure if it doesn't. The checking is more robust when the application is built with MacApp's debugging support.
You use FailNonObject to check whether an object pointer is valid when you really expect it to be valid. If the value passed is NULL or does not point to something that looks like an object, FailNonObject fails with minErr as the error message: "Unable to <x> because there is not enough memory."
FailNoReserve
The FailNoReserve routine checks whether the application's temporary reserve is low. Calling FailNoReserve causes MacApp to rebuild its memory reserves, so use it sparingly, such as when you must be sure that some reserve space is available.
FailOSErr
The FailOSErr routine tests whether the passed OSErr value is not equal to noErr. The most common value passed to FailOSErr is the result of a Toolbox call, such as FSWrite.
FailResError
The FailResError routine calls the Toolbox routine ResError to check whether there was an error caused by the last resource operation. You call FailResError after making a Resource Manager call that does not return a handle to the resource.
FailSpaceIsLow
The FailSpaceIsLow routine checks whether the low-space reserve is missing. FailSpaceIsLow causes MacApp to rebuild its memory reserves, so it should not be called indiscriminately.
You can read more about these error-checking routines in the MacApp Class and Method Reference. The temporary and low-space reserves are described in "Dynamic Memory Allocation," beginning on page 62.

You call an error-checking routine with code like the following:

fDataHandle = (Handle)GetPixPat(fRsrcID);
FailNILResource(fDataHandle);
This code calls the Toolbox routine GetPixPat to get a 'ppat' resource. It then calls FailNILResource to determine whether the call succeeded and to generate a failure and display an error message if it did not.

The Failure Routine

When an error-checking routine is called but no error occurs, it simply returns. When an error does occur, the error-checking routine calls MacApp's Failure routine to process the error. The interface to Failure is defined as follows:

void Failure(OSErr error, long message)
The parameters to Failure are discussed in the following section. The Failure routine starts the process of handling an error by retrieving the most recent failure handler from the linked list and restoring the machine state stored by the handler. Routines called after the handler was initialized are skipped, and execution continues in the failure handler's error-recovery code branch. The error-recovery code performs its cleanup, then optionally calls ReSignal to invoke the next failure handler to continue the process.

The next section describes MacApp's mechanism for displaying failure-related errors.

How Error Messages Are Displayed

If each failure handler calls ReSignal to invoke the next failure handler in the list, the handler installed by the application object's PollEvent method is eventually reached. Its error-recovery code calls the application's ShowError method, passing the same error and message parameters that were passed to the Failure routine. ShowError passes these parameters on to the ErrorAlert routine to display an alert box.

MacApp supplies several 'ALRT' resources to display error message strings to the user. These resources each have placeholders, denoted as ^0, ^1, and ^2. The MacApp constants for these alert resources, and the message strings they contain, are as follows:

phGenError
Could not ^2, because ^0. ^1.
phCommandError
Could not complete the ^2 command because ^0. ^1.
phUnknownErr
Could not complete your request because ^0. ^1.
The placeholder ^2 is called the operation (the command or operation that caused the error), ^0 is called the reason (why the operation could not be completed), and ^1 is called the recovery (what the user can do to recover from the error).

The ErrorAlert routine chooses an alert resource and fills in the placeholders based on the error and message values passed to it. ErrorAlert uses the Toolbox routine ParamText to set the alert text. The placeholders are filled in as follows:

Note
Placeholders are convenient for English language applications, but it is easier to internationalize (or localize) applications that use only complete error strings.

Setting the Error Message in a Failure Handler

After you have dealt with an error in your failure-handling code, you can either return normally by doing nothing or propagate the error by calling ReSignal. The FailInfo::ReSignal method just calls Failure to execute the next failure handler in the list, passing the same error and message values that were passed to your failure handler.

If the message parameter was already set to a nonzero value, you don't usually want to change it in your failure handler. A failure handler should generally assume that the routine that called Failure has more specific knowledge about the error, so the values it supplied for error and message are the most appropriate values.

To set the message value only if it hasn't been set already, you can call the FailNewMessage global routine instead of calling ReSignal. The FailNewMessage routine has three parameters: error, oldMessage, and newMessage. You pass the error value, the previous message value, and a newMessage value to use if the previous value is 0. FailNewMessage calls Failure, passing the old message if it isn't 0, and otherwise the new message.

Calling the Failure Routine Directly

Instead of calling one of the error-checking routines supplied by MacApp, your application can check for errors itself, then call MacApp's Failure routine if it detects an error. When you call Failure directly, you supply error and message parameters as described in "How Error Messages Are Displayed," beginning on page 564. You can either pass error and message values that are defined by MacApp, or you can define your own special messages. The following example shows how to fail with a program error instead of an out-of-memory error:

fDataHandle = YourGetDataHandleRoutine(); // Returns a data handle.
if (fDataHandle == NULL)
   Failure(minErr, 0);
In this code fragment, you expect to get the YourGetDataHandleRoutine routine to return a handle. If you call FailNIL on the returned value and the value is NULL, the error message would be "Unable to <x> because there is not enough memory." By calling Failure directly and passing minErr as the error value, the error message becomes "Unable to <x> because of a program error."

Note
When you call Failure directly, you can effectively create a "silent failure" (no message is displayed) with a call like the following:
Failure(noErr, 0);// Silent failure. u
You can also override the ShowError method in your application class to change the way error alerts are displayed.


Previous Book Contents Book Index Next

© Apple Computer, Inc.
25 JUL 1996