Learn how to take advantage of the Send Program Message (QMHSNDPM) API to alert users to problems.
In a previous article, "What to Do with Messages in the Application Program," we looked at how to detect error conditions in a program. These conditions might be the result of an API call or an error in the application program itself. Today, we will look at one approach to informing the user that a problem has been encountered.
To do this, we will use the Send Program Message (QMHSNDPM) API. The QMHSNDPM API defines several parameters, which are shown below. The full documentation for the API can be found here.
Required Parameters Group:
1 |
Message identifier |
Input |
Char(7) |
2 |
Qualified message file name |
Input |
Char(20) |
3 |
Message data or immediate text |
Input |
Char(*) |
4 |
Length of message data or immediate text |
Input |
Binary(4) |
5 |
Message type |
Input |
Char(10) |
6 |
Call stack entry |
Input |
Char(*) or Pointer |
7 |
Call stack counter |
Input |
Binary(4) |
8 |
Message key |
Output |
Char(4) |
9 |
Error code |
I/O |
Char(*) |
Optional Parameter Group 1:
10 |
Length of call stack entry |
Input |
Binary(4) |
11 |
Call stack entry qualification |
Input |
Char(20) |
12 |
Display program messages screen wait time |
Input |
Binary(4) |
Optional Parameter Group 2:
13 |
Call stack entry data type |
Input |
Char(10) |
14 |
Coded character set identifier |
Input |
Binary(4) |
Default Public Authority: *USE
Threadsafe: Yes
The first parameter, Message identifier, can be either blanks for an immediate message such as "Good morning" or the identifier of a predefined message description (such as CPF9810). While the QMHSNDPM API can send a wide variety of message types (diagnostic, completion, status, etc.), we are interested today in sending escape messages. When sending escape messages, the API requires that the message is predefined, so we will need to create a message file and a message description. The CL command to create the Application Messages message file APPLMSGF in library QGPL is this:
CRTMSGF MSGF(QGPL/APPLMSGF)
The command to add message description APP0001 to APPLMSGF is this:
ADDMSGD MSGID(APP0001) MSGF(QGPL/APPLMSGF) +
MSG('Severe error found in program &1. Notify your support personnel.') +
SECLVL('Review the job log for previous error(s) that have been logged.') +
SEV(99) FMT((*CHAR 10))
In our initial use of the QMHSNDPM API, we will use message identifier APP0001 to report unexpected errors in the running of our application.
The second parameter, Qualified message file name, is the names of the message file and the library where the message identifier specified in the first parameter can be found. We will use message file APPLMSGF in library QGPL.
The third parameter, Message data or immediate text, is used to specify replacement variable information that is to be inserted into a predefined message. Message description APP0001 defines one substitution variable (&1), which represents the name of the program encountering the failure. This substitution variable is defined as a 10-byte character variable. In our sample program, we will access the program name using RPG's Program Status Data Structure (PSDS). The message description can have up to 99 substitution variables of various data types (binary, packed decimal, character, etc.), so you have quite a bit of flexibility in what other information you might want to include in the error message text. For an immediate message, this is also where you would provide the impromptu message text (such as "Good morning").
The fourth parameter, Length of message data or immediate test, is the length of the data sent to the API using the third parameter.
The fifth parameter, Message type, identifies the type of message being sent. As mentioned in reviewing the first parameter, the QMHSNDPM API can send a variety of message types. The sample program will be sending escape messages (type *ESCAPE).
The sixth and seventh parameters, Call stack entry and Call stack counter, respectively, are used to identify where the message is to be sent. The sixth parameter identifies a call stack entry (an OPM program or ILE procedure) within the current job, and the seventh parameter identifies a relative counter based on the call stack entry identified by the sixth parameter. If, for instance, the Call stack entry identifies the current procedure calling the QMHSNDPM API and the seventh parameter is set to 0, then the message is sent to yourself. If the Call stack entry identifies the current procedure and the seventh parameter is set to 1, then the message is sent to the caller of the current procedure (the call stack entry one previous to the current procedure or OPM program). Several special values are supported for the Call stack entry parameter. The special value of asterisk (*) can be used to indicate that the current procedure (or OPM program) should be used. The asterisk (*) special value is what would typically be used in the previous example scenarios. Another special value is *PGMBDY, which indicates that the message should be sent to the oldest (or first) call stack entry for the program (or service program) currently running. It is this special value our sample program will be using along with a Call stack counter value of 1. This combination will cause the escape message sent by the sample program to be sent to the caller of the current program (or service program).
Using *PGMBDY and a Call stack counter of 1 can simplify the reporting of an error when your program has multiple procedures as the message will be sent directly to the caller of the program. This can be important when your program has a main procedure (A) that calls one or more subprocedures (B, C, D, etc). Having the subprocedure directly send the escape message to (*PGMBDY + 1) avoids having intermediate procedures "see," and then have to handle, the error message. By analogy, if you have ever had subprocedure B set on *INLR and then return to procedure A, you quickly find out that procedure A is quite happy to continue running. Procedure A, in order to stop processing, must check for *INLR and also return if the program is to end. Using (*PGMBDY + 1), you can avoid having A try to report an error that B has already reported.
The eighth parameter, Message key, is the key assigned by the system to the message being sent. This value will not be used in the sample program.
The ninth parameter, Error code, is the standard system API error code parameter.
The remaining parameters are not used by the sample program, though you may want to review them in the Information Center to get a feel for some of the other features available to you when sending a message.
Returning to the program of the previous article, at the end of the main program/procedure, there is a global on-error block to catch all unexpected errors that might occur. These might be errors such as MCH0603 – Range of subscript value or character string error, MCH1211 – Attempt to divide by zero, or a CPF message resulting from an API call where the error code Bytes provided field is set to zero. In these situations, the error message is already in the job log and we want to end the program with a message such as the previously created APP0001. The following program additions will do just that.
We will start by providing a prototype for the QMHSNDPM API. Here's the prototype, named SndMsg:
dSndEscape pr extpgm('QSYS/QMHSNDPM')
d MsgID 7 const
d QualMsgF 20 const
d MsgDta 65535 const options(*varsize)
d LenMsgDta 10i 0 const
d MsgType 10 const
d CallStackEntry 65535 const options(*varsize)
d CallStackCntr 10i 0 const
d MsgKey 4
d QUSEC likeds(QUSEC)
d LenCSE 10i 0 const options(*nopass)
d CSEQual 20 const options(*nopass)
d DSPWaitTime 10i 0 const options(*nopass)
d CSEType 10 const options(*nopass)
d CCSID 10i 0 const options(*nopass)
As we need the program name for the substitution variable &1 of message description APP0001, we will also define a program status data structure.
dPSDS sds qualified
d PgmName 334 343
And as the QMHSNDPM API provides the Message key as an output parameter, we will define a standalone 4-byte character variable MsgKey even though the sample program will not utilize the message key.
dMsgKey s 4
Optionally, the program will also define a qualified data structure to hold the substitution variables for each message that might be sent. My convention is that the name of the data structure is the same as the name of the message description.
dApp0001 ds qualified
d PgmName 10
Strictly speaking, this data structure is not necessary as there is only one substitution variable, but I find it helpful to have a standard in place that the replacement data for any given message can be found in a data structure of the same name. A data structure typically is needed when multiple substitution variables are used in a message (as they all must be passed as one contiguous value in the third parameter of QMHSNDPM), and I can then also consistently use the %size of the data structure as the fourth parameter of QMHSNDPM.
With the previous definitions in place, we will now insert a call to the QMHSNDPM API in the program's on-error block.
on-error;
// Any additional work you might want to do
App0001.PgmName = PSDS.PgmName;
SndMsg( 'APP0001' :'APPLMSGF QGPL' :App0001 :%size(App0001)
:'*ESCAPE' :'*PGMBDY' :1 :MsgKey :QUSEC);
endmon;
Now, when the program encounters any unexpected error, the on-error block will send the escape message APP0001 to the program's caller, indicating that an error occurred and that the job log contains additional information. As the message type is *ESCAPE, the program will end immediately (that is, control will not be returned to any statements following the SndMsg API call). As control will not be returned to the program, any additional processing you might want to do, for instance using the DUMP(A) operation, needs to be done prior to calling the API.
If the sample program is named SNDESC and a divide-by-zero error is encountered, the following two messages will be found in the job log.
Attempt made to divide by zero for fixed point operation.
Severe error found in program SNDESC. Notify your support personnel.
The first message is sent by the system identifying the underlying problem. The second message is sent by the application program.
The previous article also discussed, within the context of determining if an object exists and, if not, creating it, using an API error code parameter that disabled the sending of escape messages from an API. This disablement is accomplished by using an error code Bytes provided value of 8 or more.
If escape messages from an API have been disabled and an unexpected error is encountered (for instance, from the previous article, CPF9820 – Not authorized to the library), then the program needs to do more than just send the APP0001 error message. This is because the CPF9820 error message was never actually sent, so the support personnel will not find any additional information in the job log—a rather frustrating experience!
In this situation, we could call the failing API a second time, this time with escape messages enabled. But there is a better solution—namely, using the QMHSNDPM API to send the original escape information that the API would have sent had escapes not been disabled. In the next article, we will look at how to accomplish this.
In the meantime, if you have any API questions, send them to me at
LATEST COMMENTS
MC Press Online