![]() |
|
Published 1998-05-01 Printer-friendly version
We've come a long way from the sixties, where this famous quote from a popular television series was the type of error message we expected from a computer (at least when we were kids). Now, we are application developers and we have to think of the end users when we process or trap errors. Also, when we get a support call and this is the error message that is relayed to us from our program, we don't have a clue what might be the problem. Then we sound pretty lame when we inform our customer that we don't know what this error means.
In the past, we have mostly relied on Clarion to process our errors for us. A lot of times I have been asked the question: How can we change the error messages that are generated by the templates and runtime library in Clarion? My answer would always be: Change the templates (for part A) and you really can't (for part B). The end result for most of us would be to just 'go with the flow' and use the generated errors.
When we would trap our own application errors, it would be by providing a form of the MESSAGE statement. Sometimes CASE MESSAGE would be used to give the end user a choice of how to handle the error. Most of the time, we would put the same MESSAGE structure in several places in our programs with very little change in the text. Just type it over and over.
Now, we have an advantage of OOP and C4, we have an object called GLOBALERRORS that we can use to our advantage. Here is where Clarion processes the internal messages it generates, by making a call to this object. It is one of the Base Classes included with the ABC classes and is included in every program we write using the ABC templates. We can use this object like all the other objects in C4 ABC. We can change the properties and call the methods from anywhere in our application because this object is instantiated globally.
A common error that is processed by Clarion is the duplicate key error. If we use some cryptic naming conventions for the files, fields, and keys in our dictionary, the message generated can be a little confusing for our end users. For example, if we have a file named FILE1 with a key called F1:K2, this doesn't mean anything to anyone but us. The message generated by default would look like this:

We would know that we have a duplicate key, and we, as the developer, would know which key it was, and which field probably caused it. Our end user, on the other hand, would probably not be able to know what they did wrong, or what data caused the error. At least until they have encountered this a time or two. On the other hand, if they saw this:

They would have a better chance of recognizing what it was that caused it, and be able to correct the situation on their own without calling us in the middle of the night and waking us up, and depriving us of our beauty sleep. It tells them that it was a duplicate field, and tells them which field is probably the duplicate. They only have to change the text field, and the problem goes away. This would require a template change in the standard templates. OOP makes this easier now.
Another function of the Error Class is the messages that are used for things like: The delete message, when we delete a record from the browse. The message to add another record if we have this selected in the Messages and Titles properties of our form procedures. The ToDo message for procedures we have not yet defined in our application. Etc... These messages would require a template change to change the messages in the CW2.003 (and before) standard templates. These would be permanent changes and would reflect in all of our applications from that point on, or we would have to use and maintain separate template sets for separate programs. Now, we have this functionality as an object. Since it is an object, we can change the properties at runtime, and call the methods when we need to use this object. OOP makes this easier.
Finally, we have our own application specific errors that were handled with the MESSAGE or CASE MESSAGE functions before. Guess what? That's right, OOP makes this easier. We can handle all of these things with the object GOBALERRORS that the ABC template so kindly creates for us globally.
I'm going to show you how we can use the 'out of the box' errors class to create our own custom error messages and change the existing default error messages by using this Class. It is really quite easy to do. Let's look at some of the things that have been provided for our enjoyment.
AppErrors GROUP
Number USHORT(7)
USHORT(Msg:RebuildKey)
BYTE(Level:Notify)
PSTRING('Invalid Key')
PSTRING('%File key is invalid.')
USHORT(Msg:FieldOutOfRange)
BYTE(Level:Notify)
PSTRING('Range Error')
PSTRING('%Field must be between %Message')
USHORT(Msg:MyError1)
BYTE(Level:Notify)
PSTRING('This is my error number 1')
PSTRING('%Field is %Message')
USHORT(Msg:MyError2)
BYTE(Level:Notify)
PSTRING('This is my error number 2')
PSTRING('%File is locked. Wait a few minutes and '|
&'try again.')
USHORT(Msg:MyError3)
BYTE(Level:Fatal)
PSTRING('This is my error number 3')
PSTRING('This is a fatal error and will end the '|
&'program')
USHORT(Msg:UnknownError)
BYTE(Level:Fatal)
PSTRING('Unknown Error')
PSTRING('This is an unknown fatal error '|
&'and will end program')
USHORT(Msg:ProcedureToDo)
BYTE(Level:Notify)
PSTRING('Process Not Completed')
PSTRING('The process you have selected '|
&'has not been completed yet. '|
&'It will be available in a later update.')
END
This is the data structure that contains the ID, messages, and severity level of the errors. This is a sample of a structure that is used in the sample app included with this article. It can be found in the Global Data embed point. It includes the override of some Clarion messages, and the addition of some application messages. The first USHORT in the group structure is a constant that informs the object that this structure contains 7 messages. The other USHORT is repeated for each message and contains an equate for the message ID. The standard Clarion messages have the equates in the ABERROR.INC file. The datastructure for the default messages can be found in the ABERROR.TRN file. This structure, by using the same equates (ID's) as the default structure, causes my new messages to be displayed anytime the program encounters one of these situations. To add these messages to the GLOBALERRORS object, you must call the method AddErrors to include/override the messages defined in the data structure above. I placed this call in the window manager init method embed in the Main procedure in my app.
GlobalErrors.AddErrors(AppErrors)
The parameter is the label of the data structure containing my messages.
I also used the AddErrors method to override some messages at the procedure level. These were: the Duplicate Key message shown above, the Delete Record message, and the Add Another Record message. These can be found in the frmFile form procedure in the sample app. Also, when you override the messages at the procedure level, you have to remove them when you leave the procedure. So, I made a call to the RemoveErrors method in the Window Manager Kill embed. This removed my overrides and reinstated the Clarion default messages for any other procedures. In this way, you can make the standard messages custom at the procedure level and create your messages in context with the procedures.
You might notice that there is a BYTE field in each message structure that contains an equate starting with Level:. This is the severity level of the message. There are 5 levels provided by default with the class. Level:Benign, User, Notify, Fatal, and Program. Level:Benign returns Level:Benign and does no further processing. Level:User displays the message and returns Level:Benign or Level:Cancel depending on the user selection of the YES or NO button on the message window. Level:Notify displays the message and returns Level:Benign . Level: Fatal displays the message and halts the program. Level:Program is treated the same as Level:Fatal.
The methods used to handle the processing of the custom messages are:
SetField('FieldName') This method sets the value of the %field expansion macro.
SetFile('FileName') This method sets the value of the %file expansion macro
AddErrors(DataStructure) adds/overrides the messages declared in DataStructure to the error object.
RemoveErrors(DataStructure) removes the messages declared in DataStructure from the error object, and reinstates the original messages if they were overridden by an item in DataStructure.
ThrowMessage(ErrorID,'Message Text') sets the value of the %message expansion macro and processes the error.
Throw(ErrorID) process the error.
These are the methods used in the sample program provided with this article. Other methods of note are:
ThrowFile(ErrorID,'FileName') sets the value of the %file expansion macro and process the error.
SetFatality(ErrorID,SeverityLevel) changes the severity of the error specified by ErrorID.
You can look up the other methods used by this object in the Application Handbook included with C4. I have only shown those methods I have used in the sample app along with a couple of others that you might use.
To make things even easier, I created a function in the app to handle the 'Throw' methods used to eliminate even the few lines of repetitive code needed to process the errors. The structure of this function is such that it calls the appropriate 'Throw' method and the SetField, or SetFile method based on four optional parameters. The prototype of the function is:
DoError (<USHORT>,<STRING>,<STRING>,<STRING>)
The code in this function processes the proper 'Throw' method based on the inclusion of certain parameters.
IF NOT OMITTED(2)
GlobalErrors.SetField(xField)
END
IF NOT OMITTED(3)
GlobalErrors.SetFile(xFile)
END
IF NOT OMITTED(1)
IF NOT OMITTED(4)
GlobalErrors.ThrowMessage(xErrID,xMsg)
ELSE
GlobalErrors.Throw(xErrID)
END
ELSE
GlobalErrors.Throw(Msg:UnknownError)
END
As you can see, I only have these method calls in one place in the app, and each time I need to process an error or message, I just add one line of code:
DoError(Param1,Param2,Param3,Param4)
By omitting any parameter(s), I can control how and which type of error message is called.
Please play with the sample included here and see how easy it is to add your own custom errors and messages, as well as override the standard clarion errors/messages. Check out my override of the ToDo message. It can even be used as a marketing tool for your application for added functionality.
This is another example where OOP actually simplifies our life, after we learn how it works. I was dragged into the OOP world kicking and screaming when C4 first went Beta, and then I started testing and playing with it. Now, I don't know how I got along without it. So many things are so much easier to manipulate now. Even Errors and Processing Messages.
If you have any comments or questions about the sample app, or this article, please email me at gcobaugh@topspeed.com. I might even answer<g>. Until we meet again, right here in our own little corner of cyberspace, Happy Coding.
Copyright © 1999-2008 by CoveComm Inc. All Rights Reserved. Reproduction in any form without the express written consent of CoveComm Inc., except as described in the subscription agreement, is prohibited.
Clarion Magazine ISSN 1718-9942
One year: $189
(includes all back issues since '99)
Renewals from $139
Two years: $289
Renewals from $239