Handling Exceptions

This chapter explains how to handle exceptions that are raised by Acrobat core API methods. Acrobat core API methods generally do not return error codes; instead, they raise exceptions when errors occur. Exceptions are handled by exception handlers. It is recommended that you create your own exception handlers to trap and handle exceptions.

Creating exception handlers

Exception handlers typically perform object cleanup (such as releasing memory) when an exception occurs. Your exception handler can either deal with the exception or pass it along to the next handler on the exception handler stack.

You use the DURING, HANDLER, and END_HANDLER macros to define exception handlers. Application logic that may raise an exception appears between the DURING and HANDLER macros. Application logic that handles exceptions appears between the HANDLER and END_HANDLER macros.

If the method raises an exception, the handler code is executed; otherwise, it is not executed. When an exception occurs, your handler can access the exception error code by using the ERRORCODE macro. The value returned by the ERRORCODE macro does not change until another exception is raised. (See the Acrobat and PDF Library API Reference.)

The following code example declares an error handler that is active during a call to the AVDocOpenFromFile method. If an exception occurs, an error message is displayed to the user. For information about opening a PDF document by using the AVDocOpenFromFile method, see Opening PDF documents.

  1. Creating an exception handler

DURING
  AVDoc avd = AVDocOpenFromFile(asp, NULL, (char *)NULL);
HANDLER
  AVAlertNote("Error opening file");
END_HANDLER

Note

For code brevity, most of the code examples located in this guide do not include DURING, HANDLER, and END_HANDLER macros to handle exceptions. You should include these macros in your own custom code.

Returning a value from an exception handler

To return a value from a DURING HANDLER block, do not use a return statement. Instead, use the E_RETURN(x) or E_RTRN_VOID macros. The E_RETURN(x) macro returns a value and the E_RTRN_VOID macro does not return a value (this is equivalent to a void function).

The E_RETURN(x) macro must not invoke a function that might raise an exception, because the macro pops an exception frame off the stack before evaluating the expression to return. If this evaluation raises an exception, it does not call your handler, but instead calls the next handler up the stack. For example, consider the following application logic:

E_RETURN(myfn());

This is not recommended if there is any possibility that myfn could raise an exception. If you need to invoke a function, it is best to do it this way:

result = myfn();

 E_RETURN(result);

Now, if myfn raises an exception, your handler will be executed.

Raising exceptions

Although many Acrobat core API methods raise exceptions, some methods do return error codes or NULL if something goes wrong. Inside a DURING HANDLER block, if your plugin detects one of these cases, it can invoke the ASRaise method, which generates an exception. This has the effect of causing the HANDLER END_HANDLER code to execute, just as if the original method raised an exception.

You can also use the RERAISE macro when you do not want your exception handler to handle an exception, but want to pass the exception to the next exception handler on the stack.

Note

Your plugin should use the ASRegisterErrorString method to define its own exceptions.

Exception handling scenarios

This section discusses the following potential problems with exception handling:

  • Goto statements

  • Nested exception handlers in a single function

  • Register usage

Using goto statements

It is recommended that you not use a goto within a DURING HANDLER block. Jumping outside a DURING HANDLER block disrupts the stack frame, as shown in the following example:

DURING
     goto error;
 HANDLER
 END_HANDLER
 error:

This action leads to unpredictable results because the top stack frame has not been removed. As a result, the frame is incorrect. Instead, use the ASRaise method and then use the goto statement within the HANDLER END_HANDLE block, as shown in the following example:

DURING  ...
     ASRaise(myspecialerrorcode);
 HANDLER
 if ERRORCODE == myspecialerrorcode
     goto error;
 END_HANDLER
 error:

For information about the goto statement, see the Acrobat and PDF Library API Reference.

Using nested exception handlers

Avoid using nested exception handlers within a single function. The exception handling macros change the call stack, and nesting them can disrupt the stack. Your plugin can safely nest an exception handler if the nested handler is in another function called inside the DURING HANDLER block, as shown in the following example.

DURING
...
     MyFunction();
...
 HANDLER
...
 END_HANDLER
...
     void MyFunction(void)
...
 DURING
...
 HANDLER
...
 END_HANDLER
...
 }

If you insist on nesting exception handlers in a single function, do not return from the inner exception handler (either through a call to return in a handler or E_RETURN from body code). This action leaves the exception stack out of sync with the call stack. Any errors raised in body code surrounded by the outer exception handler will restore the incorrect calling environment and lead to unpredictable results, as shown in the following example.

{
 DURING /* Places one frame on the exception stack */
 pdoc = AVDocGetPDDoc(avdoc);
     DURING /* Places a second frame on the stack */
         rootBm = PDDocGetBookmarkRot(pdDoc);
         if (!PDBookmarkIsValid(rootBm)){
         E_RTRN_VOID
         /*
         Returning here interferes with the exception stack
         because two frames have been placed on the stack
         and E_RTRN_VOID only clears one of them before
         returning
         */
 }
 pdAction = PDBookMarkGetAction(parentBm);
 HANDLER
         AVAlertNote("Bad AVDoc");
         return (1);
         /*
         Returning here interferes with the exception stack
         because there is still a frame on the stack from
         the outer DURING macro and it will not be cleared
         before the function returns
         */
 END_HANDLER
 HANDLER
     AVAlertNote("Bad PDDoc");
 END_HANDLER
 }

Using register variables

The DURING and HANDLER macros use the standard C setjmp/longjmp mechanism. The DURING macro calls setjmp. An exception results in a longjmp to the context that was saved by the most recent setjmp. When a longjmp occurs, all registers, including those containing variables the compiler optimized into register variables, are restored to the values they held when the setjmp occurred.

As a result, the state of local variables that have been optimized into registers is unpredictable when the exception handler is invoked. To avoid this situation, declare all variables that are set in the main body of the code and used in the exception handler or beyond (if the handler lets execution continue) as volatile. This ensures that they are never optimized into register variables, but are always referenced from memory.

When using the volatile statement, place the keyword in the correct location, such as the following:

volatile myStruct* p = 0;

The previous statement declares the instance of the structure to be volatile. The next statement declares the pointer itself to be volatile:

myStruct* volatile p = 0;

In general, the second version is the one to use.