The examples that follow demonstrate common use cases for FINALLY blocks.

Example 1

In Example 1, the FINALLY block executes before any flow-of-control options (LEAVE, NEXT, RETRY, RETURN, or THROW) are executed for the associated block. For iterating blocks, the FINALLY block executes after each iteration of the block:

DO ON ERROR UNDO, LEAVE:
    FIND Customer WHERE CustNum = 1000. /* Raises ERROR and execution goes to
                                           FINALLY block before the LEAVE 
                                           option executes */

    MESSAGE "This message never appears because of ERROR condition." 
        VIEW-AS ALERT-BOX BUTTONS OK.

    FINALLY:
        MESSAGE "Inside FINALLY block." 
            VIEW-AS ALERT-BOX BUTTONS OK.   
    END FINALLY.  /* LEAVE DO block here */ 

END. /* DO */

MESSAGE "Out of DO block." VIEW-AS ALERT-BOX BUTTONS OK.

If you run this code, you see the following messages:

** Customer record not on file (138)
Inside FINALLY block.
Out of DO block.

Example 2

In Example 2, after ERROR is raised, execution goes to the CATCH block and then to the FINALLY block.

DO ON ERROR UNDO, LEAVE:
    FIND Customer WHERE CustNum = 1000. /* Raises ERROR and execution goes to
                                           CATCH block. */

    MESSAGE "This message never appears because of ERROR condition." 
        VIEW-AS ALERT-BOX BUTTONS OK.

    CATCH eSysError AS Progress.Lang.SysError:
        < Handler code for SysError condition >
        MESSAGE "Inside CATCH block." VIEW-AS ALERT-BOX BUTTONS OK.
        /* Execution goes to FINALLY before leaving DO block. */ 
    END CATCH.

    FINALLY:
        < Your code >
        MESSAGE "Inside FINALLY block." VIEW-AS ALERT-BOX BUTTONS OK.
        /* LEAVE DO block here. */ 
    END FINALLY.

END. /* DO */

MESSAGE "Out of DO block." VIEW-AS ALERT-BOX BUTTONS OK.

If you run this code, you see the following messages:

Inside CATCH block.
Inside FINALLY block.
Out of DO block.

Example 3

In Example 3, after ERROR is raised, execution goes to the CATCH block, which rethrows the error. The FINALLY block executes for the DO block before the ERROR is raised in the procedure block. The MESSAGE statement there does not execute because of the raised error, but the outer FINALLY runs.

DO ON ERROR UNDO, LEAVE:
    FIND Customer 1000. /* Raises ERROR and execution goes to the CATCH block. */

    MESSAGE "This message never appears because of ERROR condition." 
        VIEW-AS ALERT-BOX BUTTONS OK.

    CATCH eSysError AS Progress.Lang.SysError:
        < Handler code for SysError condition >
        MESSAGE "Inside CATCH block." 
            VIEW-AS ALERT-BOX BUTTONS OK.
        /* Execution goes to FINALLY before leaving DO block. */ 
        UNDO, THROW eSysError.
    END CATCH.

    FINALLY:
        < Your code >
        MESSAGE "Inside inner FINALLY block." 
            VIEW-AS ALERT-BOX BUTTONS OK. 
    END FINALLY.

END. /* DO */

MESSAGE “This message never appears because of ERROR thrown from CATCH block."
<other code>

FINALLY:
    < Your code >
    MESSAGE "Inside outer FINALLY block." 
        VIEW-AS ALERT-BOX BUTTONS OK. 
END FINALLY.

If you run this code, you see the following messages:

Inside CATCH block.
Inside inner FINALLY block.
Inside outer FINALLY block.