Message Data Data Structures
August 30, 2016 Hey, Ted
Sending Escape Messages from RPG is a great article. Your program defines the message data parameter as 80 bytes of character data, but the IBM Knowledge Center defines MSGDTA as char(*) with notes saying it can be up to 32,767 bytes. I would like a variable longer than 80 bytes, but instead of coding 100 today, 120 next project, and so on, how could I code it to take full advantage of the API?
You’re right that the API can handle up to 32K of data, but you need only define MsgDta as large as you need it to be for the message you’re sending. For all practical purposes, 32K is infinity. I can’t imagine a message with that much data.
Here are a few ways to take advantage of this API.
1. Put the prototype in a copybook. Some APIs are prototyped in the QSYSINC library, but I didn’t find one for QMHSNDPM.
2. Define the message data with a length of 32,767, and add OPTIONS(*VARSIZE). That allows you to pass shorter variables when you call the program.
3. In my example, I used a scalar variable, since message CPF9898 only has one substitution value. What makes more sense (to me, anyway) is to create a data structure for the message data. Each subfield of the data structure represents one of the substitution values in the message description. You might want to put these definitions in a copybook (or copybooks) as well.
Here’s an RPG example that incorporates those ideas. First, a message description.
ADDMSGD MSGID(HHH9015) MSGF(HHHMSGF) + MSG('Work order &1, operation &2 could not be completed. + Status is &3.') + FMT((*CHAR 8) (*CHAR 3) (*DEC 3))
Notice the three data values are defined as character 8, character 3, and packed 3.
Next, the prototype for QMHSNDPM:
D QMHSNDPM pr extpgm('QMHSNDPM') D MsgID 7 const D MsgFile 20 const D MsgDta 32767 const options(*varsize) D MsgDtaLen 10i 0 const D MsgType 10 const D MsgQ 10 const D MsgQNbr 10i 0 const D MsgKey 4 D ErrorDS 16
Next, appropriate fragments of the program.
/copy prototypes,qmhsndpm D ErrorDS ds 16 qualified inz D BytesProv 10i 0 D BytesAvail 10i 0 D MsgDta s 80 D MsgKey s 4 D HHH9015 ds qualified D WorkOrder 8a D Operation 3a D Status 3p 0 HHH9015 . WorkOrder = WrkOrdr; HHH9015 . Operation = OperNo; HHH9015 . Status = LastStatus; QMHSNDPM ('HHH9015': 'HHHMSGF *LIBL': HHH9015 : %len(HHH9015): '*ESCAPE': '*' : 2: MsgKey: ErrorDS);
Notice the HHH9015 data structure–character 8, character 3, packed 3. Do you see that the subfields correspond to the data definitions in the HHH9015 message description?
Notice also the length function (%LEN) in the fourth argument.
That’s the reason you can define the message data as 32,767 and yet pass shorter values without the API reading a lot of garbage from memory.
The message looks like this:
Work order T4168872, operation 210 could not be completed. Status is 400.
Even though CL doesn’t have data structures, you can accomplish the same sort of thing by using defined variables. Here’s a CL example. First, two message descriptions:
AddMsgD MsgID(GGG1010) msgf(GGGMSGF) + Msg('&3 posting is complete for company &1, period &2.') + fmt((*dec 3) (*dec 2) (*char 32)) /* &1 = company */ /* &2 = period */ /* &3 = type of posting */ AddMsgD MsgID(GGG9990) msgf(GGGMSGF) + Msg('Posting option &1 is invalid. Job canceled.') + fmt((*char 3))
Now, the program.
pgm parm(&inCompany &inPeriod &inOption) dcl &inCompany *dec 3 dcl &inPeriod *dec 2 dcl &inOption *char 3 dcl &PostType *char 32 dcl &GGG1010 *char 36 dcl &Company_1 *dec 3 stg(*defined) defvar(&GGG1010 1) dcl &Period_1 *dec 2 stg(*defined) defvar(&GGG1010 3) dcl &PostType_1 *char 32 stg(*defined) defvar(&GGG1010 5) dcl &GGG9990 *char 7 dcl &Option_2 *char 3 stg(*defined) defvar(&GGG9990 1) select when (&inOption *eq EOD) + then(ChgVar &PostType 'End of day') when (&inOption *eq EOW) + then(ChgVar &PostType 'End of week') when (&inOption *eq EOM) + then(ChgVar &PostType 'End of month') when (&inOption *eq EOY) + then(ChgVar &PostType 'End of year') when (&inOption *eq Cus) + then(ChgVar &PostType 'Custom') otherwise do chgvar &Option_2 &inOption SndPgmMsg MsgID(GGG9990) Msgf(GGgMsgf) MsgDta(&GGG9990) + MsgType(*escape) enddo endselect call ggg002r parm(&inCompany &inPeriod &inOption) call ggg003r parm(&inCompany &inPeriod &inOption) call ggg004r parm(&inCompany &inPeriod &inOption) chgvar &Company_1 &inCompany chgvar &Period_1 &inPeriod chgvar &PostType_1 &PostType SndPgmMsg MsgID(GGG1010) Msgf(GGgMsgf) MsgDta(&GGG1010) + MsgType(*comp) Endpgm
Notice the GGG1010 and GGG9990 variables. Both are overlaid with other variables in accordance with the data values in the message descriptions.
The user sees messages like these:
End of month posting is complete for company 1, period 11. Posting option EAR is invalid. Job canceled.
I’ve had good success using data structures that correspond to message descriptions. This practice and a generous use of copybooks have helped me easily integrate message-handling into applications.
Ted Holt welcomes your comments and questions. Email him through the IT Jungle Contacts page.