Like ABL errors and error objects, you can handle .NET Exception objects by any of the ABL error trapping constructs:
ON ERROR phrase
NO-ERROR option on supported statements
CATCH statement
This means that you can catch all errors with one CATCH block. The following code catches ABL system errors (Progress.Lang.SysError), ABL application errors (Progress.Lang.AppError), and .NET exceptions (System.Exception):
CATCH Progress.Lang.Error eError: ...
END CATCH.
You also can write CATCH blocks to handle .NET errors separately.
The following example shows an ABL class-based method that calls the .NET static OpenRead( ) method to open a specified file for reading and return a .NET System.IO.FileStream object for it, which the ABL method then returns. The method contains three CATCH blocks to catch any errors, starting with the .NET System.IO.FileNotFoundException, followed by any other .NET exceptions, and finally by any ABL system errors. The AVM executes the first CATCH block matching the error type that is raised by the method, as shown:
METHOD PUBLIC CLASS System.IO.FileStream OpenReadFile
(INPUT cPathname AS CHARACTER, INPUT bThrow AS LOGICAL):
DEFINE VARIABLE rFileStream AS CLASS System.IO.FileStream NO-UNDO.
CATCH System.IO.FileNotFoundException eFileNotFound:
...
IF bThrow THEN UNDO, THROW eFileNotFound. END CATCH.
CATCH System.Exception eException:
...
IF bThrow THEN UNDO, THROW eException.
END CATCH.
CATCH Progress.Lang.SysError eSys:
...
IF bThrow THEN UNDO, THROW eSys.
END CATCH.
END METHOD.
These CATCH blocks are organized in the standard fashion, where the most specialized blocks come first. (Otherwise, they can never be executed because any CATCH block for a super class before them always catches the specialized exception first.) In this case, the most likely .NET exception (System.IO.FileNotFoundException) occurs when the specified file does not exist. Note also that instead of ignoring the Exception objects after they are referenced, they can be re-thrown, depending on a condition passed into the ABL method (bThrow). Often, the decision whether to re-throw or consume an error object that is caught derives from the code in the CATCH block itself rather than being passed in as a LOGICAL parameter. In this case, the method assumes that the caller knows best whether the method should re-throw any error objects or consume then entirely within the method using garbage collection.
If you do not handle the .NET Exception object with either a CATCH block or with the NO-ERROR option, and the exception is not otherwise re-thrown by the containing ABL class or procedure using the ROUTINE-LEVEL ON ERROR UNDO, THROW statement, the AVM displays all the messages it contains to the current output device, including those in any InnerException objects. When you specify the Debug Alert (-debugalert) startup parameter or SESSION:DEBUG-ALERT is TRUE, the AVM adds the .NET stack trace to the Debug Alert information. The .NET stack trace is added both in the Debug Alert Help dialog box and in the client log (when the Client Log (-clientlog) startup parameter is specified). The top of the stack (most recent call) is displayed at the top of the trace listing.
If you handle the exception with the NO-ERROR option, as with all objects that implement Progress.Lang.Error, the messages populate the ERROR-STATUS system handle and the exception object is not accessible. These messages each represent the value of the Message property on the outer Exception object and on each InnerException object that has generated a message for the exception. These messages are identical to those retrieved using the GetMessage( ) method provided by Progress.Lang.Error and implemented by System.Exception, except that in addition to the text of the Message property for an Exception object, each message returned by GetMessage( ) also indicates the type name of the .NET object from which the message originated. For more information, see the information on GetMessage( ) in the following section.