Syntax

CATCH object-variable AS [ CLASS ] condition-class:
    .
    .
    .
END [ CATCH ] .
object-variable

The variable name that references the object caught by this block. Typically, you do not define the object-variable ahead of time with the DEFINE VARIABLE statement. The AVM recognizes a new variable name on the CATCH statement as a new object-variable definition within the current scope. Each CATCH in an associated block must have a unique object-variable. You can reuse an object-variable name in a different associated block, if its type is the same as the previous use. For all blocks with their own variable scope, such as object methods or internal procedures, a CATCH statement inside that context may reuse the same variable name as a CATCH statement outside of that context even if the type is different.

[ CLASS ] condition-class

Optionally, you can provide the CLASS keyword.

The code within a CATCH block only executes if a condition of type condition-class (or a subtype) is raised within the body of the associated block. When the condition is raised, if there is an active transaction for the associated block, the transaction is undone before the AVM begins executing the statements within the CATCH block. For more information, see the reference entries for the DEFINE VARIABLE statement and the TRANSACTION option in the DO statement in the ABL Reference.

Examples

Example 1

In the following example, the CATCH block handles any ABL system error:

DEFINE VARIABLE iCust AS INTEGER. 

ASSIGN iCust = 5000.

FIND Customer WHERE CustNum = iCust. /* Will fail */

/* Won't execute because FIND fails */
MESSAGE "Customer found" VIEW-AS ALERT-BOX BUTTONS OK.

/* The associated block for this CATCH block is the main block of the .p */ 
CATCH eSysError AS Progress.Lang.SysError:
    MESSAGE "From CATCH block..." SKIP 
             eSysError:GetMessage(1) 
        VIEW-AS ALERT-BOX.
END CATCH.

Example 2

The following example illustrates reuse of the object-variable name:

DEFINE VARIABLE oneError AS CLASS Progress.Lang.SysError.
    /* This definition is not necessary. */

DO ON ERROR UNDO, LEAVE:
    FIND FIRST Customer WHERE CustNum = 5000.

    CATCH oneError AS Progress.Lang.SysError:
        MESSAGE oneError:GetMessage(1) VIEW-AS ALERT-BOX.
    END CATCH.

    CATCH twoError AS Progress.Lang.AppError:
        MESSAGE twoError:GetMessage(1) VIEW-AS ALERT-BOX.
    END CATCH.
END. /* FIRST DO */

DO ON ERROR UNDO, LEAVE:
    FIND FIRST Customer WHERE CustNum = 6000.

    /* You can reuse an error-variable from a different 
       associated block as long as it’s the same type. */
    CATCH oneError AS Progress.Lang.SysError:
        MESSAGE oneError:GetMessage(1) VIEW-AS ALERT-BOX.
    END CATCH.

    /* NOT LEGAL: oneError was already used for a SysError,
       so it cannot be reused for an AppError. */ 
    CATCH oneError AS Progress.Lang.AppError:
        MESSAGE oneError:GetMessage(1) VIEW-AS ALERT-BOX.
    END CATCH.
END. /* SECOND DO */

PROCEDURE foo:
    FIND FIRST Customer WHERE CustNum = 7000.

   /* This IS LEGAL because a new oneError variable will be
      defined within the scope of this subprocedure so its
      type does not have to match. */
    CATCH oneError AS Progress.Lang.AppError:
        MESSAGE oneError:GetMessage(1) VIEW-AS ALERT-BOX.
    END CATCH.
END.

Example 3

An associated block may have multiple CATCH blocks, each of which handles a different error class. If an error type satisfies multiple CATCH statements, the AVM executes the code in the first CATCH block that is compatible with the error type. It does not execute multiple CATCH blocks. Therefore, if multiple CATCH blocks are specified, the more specialized error classes should come first, as shown:

FOR EACH Customer:

    < Code body of the associated block >

    /* This CATCH specifies the most specialized user-defined error class. 
       It will catch only myAppError error objects or objects derived from 
       myAppError. */

    CATCH eMyAppError AS Acme.Error.myAppError:
        /*Handler code for Acme.Error.myAppError condition. */ 
    END CATCH.

    /* This CATCH will handle Progress.Lang.AppError or any user-defined 
       application error type, except for eMyAppError which is handled
       by the preceding CATCH block. */

    CATCH eAppError AS Progress.Lang.AppError:
        /* Handler code for AppError condition. */ 
    END CATCH.

    /* This CATCH will handle any error raised by an ABL statement.
       Since it is not in the class hierarchy of AppError, this CATCH 
       could come before or after the CATCH for AppError */

    CATCH eSysError AS Progress.Lang.SysError:
        /* Handler code for SysError condition. */
    END CATCH.

    /* This is compatible with any condition object that
       implements the Progress.Lang.Error interface. All the
       above classes qualify, as well as a StopError object which
       is a SysError.  So, in this context, this CATCH block will
       only run for a .NET Exception. */ 

    CATCH eError AS Progress.Lang.Error:
        /* Handler code for any error condition. */ 
    END CATCH.

END. /* Associated Block */

Example 4

The compiler issues a warning message if a block contains a CATCH block that is not reachable. The following code produces a warning, since the CATCH of eMyAppError can never be reached:

FOR EACH Customer:
    /* Code body of the associated block */

    /* This will catch all application errors */ 

    CATCH eAppError AS Progress.Lang.AppError:
        /* Handler code for AppError condition */ 
    END CATCH.

    /* The following CATCH block will never execute, because 
       myAppError is a subtype of Progress.Lang.AppError */

    CATCH eMyAppError AS Acme.Error.myAppError:
        /* Handler code for myAppError condition */ 
    END CATCH.

END. /* Associated Block */