PreviousInheritance Tutorial (Windows & OS/2) Exception Handling Tutorial (Windows & OS/2)Next"

Chapter 8: Collections, Intrinsics and Dictionaries Tutorial (Windows & OS/2)

This tutorial is about using the collection classes. Collections are very useful in object-oriented programming. They are used to store groups of objects. You usually store objects of the same type in any particular collection, although you can also store objects of different types in a collection.

Object COBOL also enables you to store COBOL intrinsic data, which is not represented by objects, in collections. This is done by a mechanism which enables you to treat an item of intrinsic data, for example a PIC X(20), as though it were an object. You can't mix intrinsic data and objects inside a single collection, or mix different types of intrinsic data.

Finally, this tutorial covers dictionaries, which are another type of collection.

This tutorial consists of the following sessions:

Time to complete: 25 minutes.

8.1 Different Categories of Collections

The different types of collection in the Class Library can all be categorized by the following properties:

The different collection classes available are listed below, with their properties.

Bag Non-indexed, automatically growable, duplicates allowed
Array Indexed, manually growable, duplicates allowed
CharacterArray Indexed, manually growable, duplicates allowed
OrderedCollection Indexed by insertion order, automatically growable, duplicates allowed
SortedCollection Indexed by sort order, automatically growable, duplicates allowed
ValueSet Non-indexed, automatically growable, duplicate values disallowed
IdentitySet Non-indexed, automatically growable, duplicate object handles disallowed
Dictionary Indexed by key, automatically growable, duplicate key values disallowed
IdentityDictionary Indexed by key, automatically growable, duplicate key object handles disallowed

ValueSet and IdentitySet only differ in the way they determine duplicate elements. ValueSets compare the values of elements and disallow duplicate values; IdentitySets compare object references, and disallow storing the same object more than once.

Dictionary and IdentityDictionary are special types of collection which are dealt with in a later section of this tutorial. They determine duplicate keys in the same way that ValueSet and IdentitySet determine duplicate elements.

Next you will look at a simple COBOL program, coll0.cbl which illustrates some of the differences and similarities between the main types of collection. The program is not an Object COBOL class program, but procedural COBOL code which uses the Class Library collection objects.

>>To animate coll0.cbl

  1. Start the icon labeled Collections in the COBOL Workbench Tutorials group.

    This starts the on-line tutorial Collections.

  2. Select the hotspot labeled Animate coll0 (you will need to double-click or press Enter on OS/2), below the heading Using This Tutorial.

    This starts Animator V2 and loads coll0.cbl.

  3. Scroll through the working-storage section until you see the declaration for fruitData.

    This is a set of strings the program stores in different types of collection.

  4. Select Check from the COBOL menu.

    This syntax checks the programs and prepares it for execution. The execution point starts at the statement below tag A001 (invoke Array "ofReferences"...). You can create any type of collection, apart from a CharacterArray, as a collection of references or a collection of values. A collection of references stores objects; a collection of values stores COBOL intrinsic data, which is explained later in this chapter.

  5. Step through the statements from A001 to A005 to create instances of the different types of collection.

    You need to specify an initial size for all types of collection. In the case of an Array, you can't exceed the initial capacity of the collection unless you send it the "grow" message. Other types of collection will grow in size if you add more elements than initially specified.

    However, growing collections can be an expensive operation at run-time, so you should still always try to pick an initial size which will reduce the number of times a collection needs to grow.

  6. Step through the statements below tag A006 (move 20 to i).

    Data item i holds the length for the instances of CharacterArray which will be created as elements for the different types of collection.

  7. Step the statement below tag A007 (invoke CharacterArray "withLengthValue"...).

    A CharacterArray is an object for storing strings. The "withLengthValue" message creates a new instance of CharacterArray and initializes it with some data.

  8. Step the statements below tag A008 (invoke aBag "add"...) until the end-perform statement.

    With all the different collection types, except Array, you use the "add" message to add new elements. With the Array, which does not grow automatically, you use "atPut" which stores the element at the specified index position.

    Although OrderedCollection is indexed, you can't use "atPut" until you have added elements to the collection. For example, once you have used "add" to add the first five elements, you can use "atPut" and an index between one and five to replace any of those elements. You can't do an "atPut" with an index greater than five until you have added more elements.

    You can't ever use "atPut" to put an element in a SortedCollection. A SortedCollection uses the value of each element to determine its position in the collection.

  9. Move the text cursor down to the statement below tag A010 (move 4 to loopCount), and select Run to cursor from the Execute menu.

    This completes execution of the perform loop without you needing to step through each statement in turn.

  10. Step the statements up to tag A011 (display " ").

    This retrieves the object reference to the fourth element in the Array instance. Sending the "display" message to the CharacterArray displays it on the screen. Its contents should be the word "banana".

  11. Step the next statement (invoke aBag "includes"...).

    Because a Bag is not indexed, you can't retrieve elements from it directly. Instead, you can query it to find out whether it has one or more occurrences of an object with a given value.

    The "includes" message returns 1 if a Bag contains an object with a matching value and 0 if it doesn't. You can also examine the contents of unindexed collections (Bags and ValueSets) by using the iterator methods. These are covered later in this tutorial.

  12. Step the statements up until tag A012 (display " ").

    These test the result of the returned value and tell you whether or not the bag contained an object with a matching value.

  13. Step the statement below tag A012 (invoke aBag "add"...).

    This adds the string to the Bag again. A Bag stores objects with duplicate values by recording the number of occurrences of each object with a different value.

  14. Step the statement below tag A013 (invoke aBag "occurrencesOf"...).

    The "occurrencesOf" message returns the number of elements a collection has which match the specified object in value.

  15. Step the statements up until tag A014 (display " ").

    This displays the information that this Bag has two occurrences of the specified string.

  16. Step the statements between tag A014 (invoke anArray "occurrencesOf"...) and A015 (display " ").

    This code demonstrates how the Array also responds to the "occurrencesOf" message. You can use "occurrencesOf" and "includes" on indexed collections as well as instances of Bag and ValueSet. This tells you whether or not the element exists, but not its position.

  17. Step the first statement below tag A015 (invoke aValueSet "add"...).

    Instances of ValueSet do not maintain duplicate elements. The ValueSet already contains an element with value "banana", so it will not be added a second time.

  18. Step the next statement (invoke aValueSet "occurrencesOf"...).

    This message always returns 1 or 0 for ValueSet instances.

  19. Step the other statements to tag A016 (display " ").

    This displays the result of the "occurrencesOf" message.

  20. Select Run from the Execute menu to execute the rest of the code in the program.

    The statements below tag A016 (display "Collection contents") display every string in the OrderedCollection and SortedCollection in indexed order. The elements in the OrderedCollection appear in the order in which they were added. The elements in the SortedCollection appear sorted into ascending alphabetical order. Ascending order is the default for a SortedCollection.

  21. Select Exit from the File menu to quit Animator V2.

At the end of this section, you may have some questions to ask:

Program coll0.cbl stores instances of CharacterArray in the collections it creates. An instance of a CharacterArray is a simple object, with an obvious single value (the string you store in it). But if you stored Account objects, like the ones used in the tutorial Inheritance in a collection, how would you determine the value? Would it be the name, the balance or the account number?

The answer is that the collection objects provide a framework within which objects stored as elements must work. When a collection needs to know whether two objects are equal, it sends one object the "equal" message, passing it the other as a parameter. It is then up to the object to interrogate the other element and decide whether they are equal or not.

The default sort method for the SortedCollection works in a similar way. The SortedCollection sends one element the "lessThanOrEqual" method and a second element as a parameter. The receiving element can then compare itself to the second element and return a result.

If you are writing your own objects to store in collections, you may need to implement these methods yourself, unless you are subclassing from a class like CharacterArray, which implements them for you. There is also a default "equal" method in Base which compares the object handles of two objects. This implementation of "equal" will only find two elements equal if they are actually the same object.

For full information on the methods you might need to implement to use the Collection classes, see the chapter Collection Frameworks.

8.2 Using Intrinsics

In the previous session, you looked at a program which stored objects in different types of collection. There may be occasions when you want to store intrinsic COBOL data, like numbers, in collections. You can do this by using the intrinsic classes of the Class Library.

Object COBOL provides a mechanism which enables you to send a message to an intrinsic data item, as though it were an object.

>>To send a message to an intrinsic data item

  1. Clone an intrinsic class to create a new class for the type and length of intrinsic data you are using.

    The class library includes classes for three different types of intrinsic data (PIC X, PIC X COMP-X, PIC X COMP-5). These classes are templates which handle data of fixed length. When you clone the class, you specify the actual size of data you want to handle.

  2. Send messages to intrinsic data items using the INVOKE ... AS statement.

The example below sends the "hash" message to a numeric data item:

00001 working-storage section.
00002 01  aValue          pic x(2) comp-5.
00003 01  cloneX2Class    object reference. 
00004 01  aLength         pic x(4) comp-5.
       ...

00005 procedure division.
       ...
00006     move 2 to aLength
00007     invoke COBOLCOMP5 "newClass" using aLength
00008                            returning cloneX2Class
       ...
00009     invoke aValue as cloneX2Class "hash"
00010                         returning aHashValue
       ...

This is what the code above is doing:

Lines 1-4 Declaring data.
Lines 6-8 Cloning the COBOLCOMP5 to create a new class for comp-5 data items two bytes long.
Line 9 Sending a message to the data in data item aValue.

The invoke...as statement uses the data item as an instance of the cloned class. You can think of an intrinsic object as a static object which has memory allocated to it at compile time. Static objects do not have object handles, unlike the dynamic objects created by the OO RTS at run-time. Intrinsic data objects are the only examples of static objects in Object COBOL.

We will now look at a short sample program, coll1.cbl, which uses the intrinsic classes to store a set of integers in an array.

>>To animate coll1.cbl

  1. If you have not already done so, start the icon labeled Tutorial 5 in the COBOL Workbench Tutorials group (starting this icon was the first instruction for animating coll0.cbl earlier in this tutorial).

    This starts the on-line Collections tutorial.

  2. Select the hotspot labeled Animate coll1 (you will need to double-click or press Enter on OS/2), below the heading Using This Tutorial.

    This starts Animator V2 and loads coll1.cbl.

  3. Select Check from the COBOL menu.

    Animator V2 syntax checks the program and prepares it for execution.

  4. Step the two statements below tag A001 (move 4 to i).

    The "newClass" message creates a clone of the CobolComp5 class, initialized in this case for data four bytes in length. The object returned is a new class object, and not an instance of CobolComp5.

  5. Step the two statements below tag A002 (move 10 to i).

    This creates an instance of Array with space for ten elements. This time the message to create the Array instance is "ofValues" (in the example in the previous section it was "ofReferences").

    When a collection is created with the "ofValues" message, it stores intrinsic data instead of object handles. The cloned class, PicX4Comp5, is used as a template so that the Array knows how much space to allocate for each element.

    You can't mix objects and intrinsics inside a single collection. Once you create a collection you can only store the type of data for which it is initialized. If you want to mix many different kinds of data in a collection, you should create the collection using "ofReferences", and use different types of objects to represent the different types of data. There is no restriction on mixing different kinds of object inside a collection of references.

  6. Step the first statement below tag A003 (move 10 to element).

  7. Select Run through from the Execute menu while the execution point is on the next statement (perform varying from 1 ...).

    This executes the entire perform loop to initialize the array.

  8. Step the statements below tag A004.

    This retrieves the fourth element from the array (which has a value of 7) and displays it. Because a data item is returned, we can show it using the display verb; when we got back objects in the previous example we had to send them the "display" message to show them.

  9. Select Exit from the File menu to close Animator V2.

This completes this part of the tutorial on using intrinsic data. For more information, see the chapter Intrinsic Frameworks.

8.3 Associations and Dictionaries

Dictionaries are a special sort of indexed collection, which store key-data pairs (known as associations). In a dictionary, the key is used as the index when you store or retrieve the data. Dictionaries do not allow you to store duplicate keys.

Like the other collection types, you can store either objects or intrinsic data in a dictionary. In a dictionary though, either the key or the data part can be an intrinsic or an object. This gives you four possible combinations for intrinsic or object storage:

When you create a dictionary you have to give it a template so that it knows how the key and data portions are to be stored. The template is either the Association class, or a clone of the Association class.

The Association class is another clonable class, like the classes for intrinsic data classes, used for creating templates for data storage. An Association template actually consists of two templates; one for the key and one for the data. To create any type of dictionary you need to create an Association template.

>>To create an Association template

  1. If the key is to be intrinsic data, clone an intrinsic class to create a template for the type and length of intrinsic data of your key.

  2. If the data is to be intrinsic data, clone an intrinsic class to create a template for the type and length of intrinsic data of your data.

  3. Clone the Association class. Send it the "newClass" message, with templates for the key and data part of your association as parameters. If either of these is an object rather than intrinsic data, use an object reference set to null as the template.

    If both the key and data entries in the dictionary are to be objects you can use the Association class itself as a template; you don't need to create a clone.

Having created a template for your dictionary object, there are two methods you can use to create a dictionary itself; "ofValues" or "ofAssociations". A dictionary "ofValues" stores each element as a key data pair. A dictionary "ofAssociations" stores each element as an instance of the Association template you used to create the dictionary.

A dictionary "ofAssociations" in effect stores three items for every entry in the dictionary:

A dictionary "ofValues" stores the key and data directly without wrapping them inside an association object. When would you use a dictionary "ofAssociations" and when would you use a dictionary "ofValues"?

A dictionary "ofValues" is more efficient at run-time in terms of speed and memory if you simply want to store data items against key items. However, if your application uses associations elsewhere to manage key/data pairs, a dictionary "ofAssociations" is a better choice as the actual dictionary only stores the object handles to the associations, and you don't have to extract the key and value from the association before putting it in the dictionary.

The sample code below creates an association template and then uses it to create a dictionary "ofValues":

00001  working-storage section.
00002  01  aKeyTemplate      object reference.
00003  01  aDataTemplate     object reference. 
00004  01  anAssocTemplate   object reference.
00005  01  aDictionary       object reference. 
00006  01  aLength           pic x(4) comp-5.
 
    ...

00007  procedure division.
        ...
00008      move 3 to aLength
00009      invoke CobolCompX "newClass" using aLength
00010                               returning aKeyTemplate
00011      move 20 to aLength
00012      invoke CobolPicX "newClass" using aLength 
00013                             returning aDataTemplate 
00014      invoke Association "newClass using aKeyTemplate
00015                                         aDataTemplate
00016                               returning anAssocTemplate
00017      invoke Dictionary "ofValues" using anAssocTemplate aLength
00018                               returning aDictionary 
         ...

Lines 1-6 Declares storage for the templates.
Lines 8-9 Creates a template for a PIC X(3) COMP-X numeric key.
Lines 11-12 Creates a template for a PIC X(20) data portion.
Line 14 Creates an association template.
Line 17 Creates a dictionary of values with an initial size of 20 elements. This will grow to accommodate extra elements once you get beyond size 20. In the current implementation of the Dictionary class, each grow operation approximately doubles the size of the dictionary; this may change in future releases.

In the next part of this section we will animate through some code which uses a dictionary to store Account objects. The Account objects are the ones which were introduced in the tutorial Inheritance.

In this section we will also introduce the Bank application. This provides a GUI front-end for creating and working on Account objects. The Bank application is used for the next few tutorials as it demonstrates many features of object-oriented programming with Object COBOL.

In this part of the tutorial the Bank application is used to demonstrate use of a dictionary. Each account created is stored in the dictionary with the account number as its key.

>>To start the Bank application

  1. If you have not already done so, start the icon labeled Tutorial 5 in the COBOL Workbench Tutorials group (starting this icon was the first instruction for animating coll0.cbl earlier in this tutorial).

    This starts the on-line Collections tutorial.

  2. Select the hotspot labeled Browse Bank application (you will need to double-click or press Enter on OS/2), below the heading Using This Tutorial.

    This starts the Class Browser and loads all the files for the Bank application.

  3. Select Check All from the COBOL menu.

    There will be a short delay while all the files are syntax checked ready for execution.

  4. Select the BankApplication class from the class/program selector.

  5. Select Animate from the COBOL menu.

    Animator V2 starts and loads bankapp.int (the BankApplication class) to execute.

  6. Select Set advanced from the Breakpoint menu.

    The only code we want to animate is the code for creating and using a Dictionary. We can do this by setting a program breakpoint on the AccountManager class. This is the class which stores and retrieves accounts.

  7. Select the Program radio button, type accmgrd into the parameter field and push the Set button.

    The executable code for the AccountManager class is in file accmgrd.int.

  8. Select Run from the COBOL menu.

    The main Animator V2 window is replaced by the Run window, with the message "Executing: BANKAPP.INT". The Bank window appears. The Run window message changes to: "Breakpoint encountered".

  9. Push the OK button on the message box.

    The Animator V2 window reappears with the execution point on the first statement of the "new" method for the AccountManager class (invoke super "new"...).

  10. Step the first and second statements (invoke lsAccountManager "initialize").

    The execution point moves to the statement below tag M002 in the "initialize" instance method of AccountManager. The initialize method creates an instance of Dictionary, with pic x(8) intrinsic data as the key and objects as the data.

  11. Step the first statement (set nilReference to null).

    This sets an object reference data item to null. This will be used to set the data part of the template.

  12. Step the next two statements (move 8 to i).

    This clones the CobolPicX class to create a template for strings eight bytes long.

  13. Step the statement below tag M003 (invoke Association "newClass"...).

    You have now created an Association template for the key-data pairs to be stored in the dictionary.

  14. Step the statement below tag M004 (invoke Dictionary "ofAssociations"...).

    This creates a dictionary which the AccountManager will use to store the account data.

  15. Step through the other statements in this method up to and including exit method.

    The rest of this method sets up an ExceptionHandler for the dictionary; exception handling is the subject of a later tutorial.

  16. Select Run from the COBOL menu.

    The Animator V2 Run window reappears.

  17. Push the Open button on the Bank window.

    The Open Account dialog box appears.

  18. Type "Fred" in the name field and "10.00" in the Opening Balance field, then push the OK button.

    A message box informs you that account 1000000 has been created.

  19. Push the OK button to dismiss the message box, then push the OK button on the Animator V2 Run window.

    The main Animator window reappears, with the execution point on the first statement of the AccountManager "add" method (if lsAccount < > null).

  20. Step through the first two statements (invoke lsAccount "whatNumber"...).

    This returns the account number from the account object. This is going to be used as the key to store the account.

  21. Step through the Account code which returns the account number, until you return to the next statement in the AccountManager "add" method.

  22. Step the next statement (invoke accountCollection "atPut" using...).

    This stores the account in the dictionary using the account number as the key.

  23. Select Run from the COBOL menu.

    The Animator V2 Run window reappears.

  24. Push the Get Account button on the Bank window.

    This opens the Retrieve Account dialog box.

  25. Push the OK button.

    The Account number field is already filled with "1000000". This is the number of the account you just created.

  26. Push the OK button on the Animator V2 Run window.

    The Animator V2 main window reappears with the execution point inside the "getAccount" method of AccountManager. The account number for retrieval is passed to this method as a method.

  27. Step the first statement of this method (invoke accountCollection "at"...).

    This retrieves the account from the dictionary, and puts the reference in anAccount.

  28. Select Run from the COBOL menu.

    The Animator V2 Run window reappears. The BankApplication also opens the Account Details dialog box, which shows you the information held in the account object.

  29. Push the Cancel button to dismiss the dialog box. Don't shut down the Bank application, as we will use it in the next section to illustrate the collection iterator methods.

This completes this section; in the next section you will use the already running Bank application to look at the iterator methods for collections.

8.4 Iterator Methods

The collection classes all provide iterator methods which enable you to examine all the elements of a collection. There are four iterator methods, and they are supported by all types of collection:

This section shows how the AccountManager class uses the iterator methods to carry out operations on all accounts. The instructions below assume that you are continuing directly from the end of the previous section, and have not closed down the Bank application or Animator V2.

>>To see the use of iterators

  1. Push the Open button on the Bank window, and fill in the Open Account dialog box to create a Savings account for Wilma.

  2. Push the OK button on the Animator V2 Run window to bring back the main Animator window.

  3. Move the text cursor down to the statement below tag M020 (invoke Callback "new"...), and select Set from the Breakpoint menu.

  4. Select Set advanced from the Breakpoint menu, select the Program radio button, and push the Unset button.

  5. Select Run from the Execute menu.

    The Bank application will now execute without interruptions until you use the Print Statements function.

  6. Select Print Statements from the File menu.

  7. Push the OK button on the Run window to bring back the main Animator V2 window.

    The execution point is on the statement below tag M020.

  8. Step the first statement.

    This creates a Callback for the "printAccount" method in the AccountManager class. The literal "printAccount " in the program has a space at the end to enable the "new" method in Callback to find the end of the string.

    A Callback is an object which contains a message, and the handle of an object which will respond to the message. When you send the "invoke" message to a Callback, it sends the stored message to the stored object. For more information about using Callbacks, you can read the chapter Callback Frameworks.

  9. Step the next statement (invoke accountCollection "do" ...).

    This passes the Callback instance to the "do" method as a parameter. The "do" method has a returning parameter (in this case temp) so that you can pass back a result from your Callback at the end of the iteration if necessary. The AccountManager doesn't use this facility, but must still allocate a data item for the returning parameter as a place-holder.

    The execution point switches to the "printAccount" method of AccountManager. This has been passed the first account in the accountCollection dictionary as a parameter (in lsAccount).

  10. Move the cursor to the exit method statement, and select Run to cursor from the COBOL menu.

    This method sends the "printStatement" message to the account. A text window appears, displaying a brief statement for the account.

  11. Step the exit method statement.

    The execution point jumps back to the start of the "printAccount" method, with the next account as a parameter.

  12. Repeat steps 10 and 11 above once more to print the other account (we have only created two altogether).

    After the last exit method, the execution point returns to the "printAllStatements".

  13. Step the next statement (invoke printMethod "finalize").

    This destroys the Callback object now that we have finished with it.

  14. Move the text cursor to the first statement of the "retrieve" method (move length of lsName to i). The "retrieve" method is the second method after "printAllStatements" where you are now, so you may need to page down to get there quickly.

  15. Select Set from the Breakpoint menu.

  16. Select Run from the Execute menu.

  17. Push the Query button on the Bank window to open the Enter Name dialog box.

    This function searches through the accounts looking for those with names that match.

  18. Type "Wilma" into the Name field and push the OK button.

    The execution point switches to the first statement of the AccountManager "retrieve" method.

  19. Step through until you reach the first statement below tag M050 (invoke Callback "new"...).

    This creates a string object containing the name for which you are going to search.

  20. Step the statement to create the Callback.

    This passes nameString as a parameter to the "new" message to create the Callback. Every time the method in the Callback ("searchAccounts") is invoked, it will get nameString passed to it ahead of any other parameters sent with the "invoke" message.

  21. Step the statement below tag M051 (invoke accountCollection "select"...).

    Execution jumps to the first statement of the AccountManager "searchAccounts" method. This has been passed the string object with the name we are looking for, and the first account in the collection.

  22. Move the text cursor to the statement if acName = srchName, and select Run to cursor from the Execute menu.

    At this point, acName and srchName contain the name from the account and the name you are searching for. Both have been set to uppercase to avoid case mismatches.

    If you double-click on acName, you will see that it contains "FRED".

  23. Step the next statement.

    The test fails.

  24. Step through until the exit method statement.

    This sets the level 88 item noMatch to true. When you exit the method, it will return a 0 to the code in the Dictionary "select" method which is performing the iteration.

  25. Step the exit method statement.

    Execution returns to the top of the "searchAccounts" method, as the collection invokes it with the next account.

  26. Step through the code for the method.

    The names match this time, as this is the account for Wilma. The method returns a value of 1 to the dictionary, so Wilma is added as an element to the collection of results being created.

    When you step the exit method statement, execution returns to the statement below tag M052. The result of the "select" is a new Dictionary, which contains only the elements which were marked as true (a value of 1 was returned by the selection Callback).

  27. Step the next statement (invoke resultDictonary asOrderedCollection...).

    The "select" and "reject" methods always return a collection of the same type as the original collection, in this case a Dictionary. This message copies the data part of the dictionary into an OrderedCollection.

    The other statements in this method "finalize" all the objects created by this method which are no longer needed.

  28. Select Run from the Execute menu.

    The Bank application opens a dialog box with all the matching accounts shown in a list box.

  29. Push the Cancel button, then select Exit from the Bank window File menu to close down the Bank application.

  30. Push the OK button on the Animator V2 Run window, then select Exit from the File menu to close down Animator V2.

  31. Select Exit from the Browser File menu.

  32. Select Exit from the on-line help File menu to close down the on-line tutorial.

This completes the tutorial.

8.5 Summary

This tutorial covered the following:


Copyright © 1999 MERANT International Limited. All rights reserved.
This document and the proprietary marks and names used herein are protected by international law.

PreviousInheritance Tutorial (Windows & OS/2) Exception Handling Tutorial (Windows & OS/2)Next"