I made small change in program that sends program messages (SNDPGMMSG) to message subfile in RPG program. Change was converting subroutines to procedures.
I changed one subroutine to dcl-proc from sub-routine. Result: I stopped seeing messages in message subfile. Variables are all in global scope so this should not be the problem. (I realize that this is not best practice, but step 1 was to change subroutines to dcl-procs and make sure nothing broke.)
Code is based on example here: .aspx?cid=149
dcl-pr SFMSGSFLCL extpgm('SFMSGSFLCL');
MsgID char(7);
Msgf char(10);
MsgOpt char(1);
end-pr ;
dcl-s MSGID char(7);
dcl-s MSGF char(10);
dcl-s MSGOPT char(1);
//---------------------------------------
begsr SHOWMSG;
EVAL MSGID = S_MSG;
EVAL MSGF = 'AM_MSGF';
EVAL MSGOPT = 'I';
Exsr Send
//Send(); // fail
endsr;
//---------------------------------------
// messages DO NOT show up in message subfile
//dcl-proc Send;
// SFMSGSFLCL(MSGID:MSGF:MSGOPT);
//end-proc;
//------------------------------------------
// messages DO show up in message subfile
Begsr Send;
SFMSGSFLCL(MSGID:MSGF:MSGOPT);
Endsr;
//--
I made small change in program that sends program messages (SNDPGMMSG) to message subfile in RPG program. Change was converting subroutines to procedures.
I changed one subroutine to dcl-proc from sub-routine. Result: I stopped seeing messages in message subfile. Variables are all in global scope so this should not be the problem. (I realize that this is not best practice, but step 1 was to change subroutines to dcl-procs and make sure nothing broke.)
Code is based on example here: https://www.go4as400/chapterload.aspx?cid=149
dcl-pr SFMSGSFLCL extpgm('SFMSGSFLCL');
MsgID char(7);
Msgf char(10);
MsgOpt char(1);
end-pr ;
dcl-s MSGID char(7);
dcl-s MSGF char(10);
dcl-s MSGOPT char(1);
//---------------------------------------
begsr SHOWMSG;
EVAL MSGID = S_MSG;
EVAL MSGF = 'AM_MSGF';
EVAL MSGOPT = 'I';
Exsr Send
//Send(); // fail
endsr;
//---------------------------------------
// messages DO NOT show up in message subfile
//dcl-proc Send;
// SFMSGSFLCL(MSGID:MSGF:MSGOPT);
//end-proc;
//------------------------------------------
// messages DO show up in message subfile
Begsr Send;
SFMSGSFLCL(MSGID:MSGF:MSGOPT);
Endsr;
//--
Share Improve this question edited Mar 23 at 14:09 mortimer asked Mar 22 at 17:17 mortimermortimer 896 bronze badges
4 Answers
Reset to default 2I`s a matter of scope. See example from Mark Murphy
Procedures are needed to avoid making a large number of global variables, and to make those variables that are needed only in a given procedure local, limiting their scope to the procedure itself.
This is how they differ from subroutines, which always work with a variable in the scope where they are declared (global or local, if the subroutine is part of a procedure). Subroutines, unlike procedures, do not have their own scope.
Those variables that are outside the scope of the procedure, but are necessary for its operation, are passed to the procedure as parameters (with the necessary options specified, such as const for variables that should not change within the procedure, value if you need to pass only the value of the variable, and not a reference to it, etc.)
In your case, it makes sense to do this:
dcl-s MSGID char(7);
dcl-s MSGF char(10);
dcl-s MSGOPT char(1);
[...]
MSGID = S_MSG;
MSGF = 'AM_MSGF';
MSGOPT = 'I';
Send(MSGID: MSGF: MSGOPT);
[...]
// =======================================
dcl-proc Send;
dcl-pi *n;
MsgID char(7);
Msgf char(10);
MsgOpt char(1);
end-pi;
dcl-pr SFMSGSFLCL extpgm('SFMSGSFLCL');
MsgID char(7);
Msgf char(10);
MsgOpt char(1);
end-pr ;
SFMSGSFLCL(MsgID: Msgf: MsgOpt);
return;
end-proc;
Subfile messages have to be sent to the same procedure that opened the file. If your file is defined in the main source section, it would be opened by the main procedure. The messages should be sent to that procedure.
The name of the main procedure is in the *PROC subfield of the Program Status Data Structure.
dcl-ds pgm_status psds qualified;
main_proc_name *proc;
end-ds;
I will write a dissenting opinion here. I don't think it is scoping in this case. The variables are all defined at global scope, and sub-procedures can use that. Instead, it is the message queue you are writing your messages to. The problem is that subroutines do not add any steps to the call stack, while sub-procedures do. The defaults for the CL command SNDPGMMSG send the message to the caller. The next step up in the call stack. In the case of subroutines, that is the program message queue. In the cast of sub-procedures, that is the message queue of the calling procedure, and there is no real telling how many levels deep you are when you send the message. So the best way I have found is to explicitly tell which message queue you want to send the message to. I use the system API to do that.
Sending messages to the Display File (DSPF) message subfile is easy once you have all the pieces lined up. But there are a lot of pieces, and I keep most of them in service programs so I can call them from whatever program I need them in. Since sending messages to the user is common in a 5250 program, I use these procedures in just about every 5250 program I write. I also use the snippets feature of RDi to keep templates so I largely already have a shell and don't need to think about the mechanics of it much. Anyway here is how I use message subfiles in 5250 programs.
First I need the message subfile defined in the DSPF, this is nearly always the same.
A* ========================================================================
A* Message Subfile
A* ------------------------------------------------------------------------
A R MSGSFL SFL
A SFLMSGRCD(24)
A MSGKEY SFLMSGKEY
A PGMQ SFLPGMQ
A* ------------------------------------------------------------------------
A* Message Subfile - Control forrmat
A* ------------------------------------------------------------------------
A R MSGCTL SFLCTL(MSGSFL)
A KEEP
A OVERLAY
A SFLINZ
A SFLPAG(1)
A SFLSIZ(2)
A SFLDSP SFLDSPCTL
A 52
AON52 SFLEND(*PLUS)
A PGMQ SFLPGMQ
The only thing that would change is the SFLMSGRCD in a 27x132 screen. In that case the line number would be 27 vs. 24.
Note the use of indicator 52. It is not removed from available indicators here. It can still be used in other parts of the DSPF if you need it because SFLEND(*PLUS) is selected regardless of the value of the indicator.
The key moving part here is the field PGMQ. That tells the message queue that will be displayed when MSGSFLCTL is written. You don't have to manage the subfile at all, just send messages to the appropriate queue.
The RPG to define the DSPF looks like this:
dcl-f pgm0001d Workstn Qualified
Sfile(msgsfl: m1);
dcl-ds msgout LikeRec(pgm0001d.msgctl: *output) Inz;
dcl-s m1 Int(5) Inz(0);
I have intentionally left out everything not associated with the message subfile, other subfiles or output formats would also need to be defined with LikeRec
and Sfile
lines, but they are not important to this discussion.
I Qualify all of my files these days. It helps avoid renames and prefixes. One consequence of using Qualified is that the K indicators for command keys do not work. Not a biggie, I use the feedback area to retrieve the AID Byte.
You only need to initialize the message subfile once in your program initialization sub-procedure. I retrieve the program name from the Program Status Data Structure (PSDS) to do this.
// Initialize Program Message Queue
msgout.pgmq = psds.MainProc;
As an exercise, I leave it to you to find the location in the PSDS. I store this format and other useful structures like feedback areas in copy books so I don't have to keep defining them all the time.
When I want to send a message to the message subfile, I use a sub-procedure from one of my service programs like this:
SendDspfMsg(psds.MainProc: 'CNV9902': msgData);
Notice that the first parameter of the sub-procedure is the same value I initialized the message subfile with. That's the link between the two. The final two parameters are a message ID and message data for the message I want to send from a message file. I define most of my messages in a message subfile, I find that to be a better practice than using ad-hock messages. Particularly when something needs to be changed like a spelling error, or text to make the message more clear. I know exactly where to go to make the change vs. having to search through program source to find an ad-hock message. It also allows me to use second level message text to further explain the error to the user, or give hints on how to troubleshoot.
The last piece needed in the program is to clear the message subfile after displaying it but before processing the user input so you don't see old messages the next time the screen is displayed. I do that with another sub-procedure from my service program.
ClearDspfMsg(psds.mainProc);
Once again I tell it which message queue I am looking at in the parameter. These two sub-procedures are all I need to manage my message subfiles.
The definitions of these sub-procedures are pretty simple:
// ------------------------------------
// Send Message to Display File (MSGID)
// Sends a message to the display file message subfile
//
// Parameters:
// pgmq - Program message queue. This must be the same as the pgmq
// specified in the display file.
// messageId - The message id of the message to be sent
// messageData - Message data for replacement values in the message. Format
// of the message data is defined by the message. This is
// optional, if missing, blanks are used.
// messageFile - The qualified name of the message file containing the
// message. The first 10 characters is the messafe file name,
// the second 10 characters is the library. This is optional,
// if blank, CNVMSG in *LIBL is used.
// ------------------------------------
dcl-proc SendDspfMsg Export;
dcl-pi *n;
pgmq Char(10) const;
messageId Char(7) const;
messageData Varchar(256) const options(*varsize: *nopass);
messageFile LikeDs(qualName_t) const options(*nopass);
end-pi;
dcl-ds msgf LikeDs(qualName_t) Inz(*likeds);
dcl-ds ec LikeDs(errCode_t) Inz(*likeds);
dcl-s msgData Char(256) Inz('');
if %parms() >= %parmnum(messageData);
msgData = messageData;
endif;
if %parms() >= %parmnum(messageFile);
msgf = messageFile;
else;
msgf.name = 'CNVMSG';
endif;
qmhsndpm(messageId: msgf: msgData: %size(msgData): '*INFO': pgmq: 0: '': ec);
// TODO Provide error checking here
end-proc;
// ------------------------------------
// Clear Display File Messages
// Clears the messages in the display file message subfile
//
// Parameters:
// pgmq - Program message queue. This must be the same as the pgmq
// specified in the display file.
// ------------------------------------
dcl-proc ClearDspfMsg Export;
dcl-pi *n;
pgmq Char(10) const;
end-pi;
dcl-ds ec LikeDs(errCode_t) Inz(*LikeDs);
qmhrmvpm(pgmq: 0: '': '*ALL': ec);
// TODO Provide error checking here
end-proc;
The templates qualName_t
and errCode_t
are in my copy book that defines common structures. They look like this:
dcl-s name_t Char(10) Template;
dcl-ds qualName_t Qualified Template;
name Like(name_t) Inz('');
library Like(name_t) Inz('*LIBL');
end-ds;
dcl-ds errCode_t Qualified Template;
bytesProvided Int(10) Inz(%size(errCode_t));
bytesAvail Int(10) Inz(0);
messageId Char(7) Inz('');
messageData Char(256) Pos(17) Inz('');
end-ds;
Prototypes for qmhsndpm
and qmhrmvpm
are also in a copy book along with the structures that they need.
dcl-ds callStackEntryQual_t Qualified Template;
module Like(name_t) Inz('*NONE');
program Like(name_t) Inz('*NONE');
end-ds;
// ------------------------------------
// QMHRMVPM - Remove Program Message
// See the Knowledge Center for descriptions of parameters
dcl-pr qmhrmvpm ExtPgm('QMHRMVPM');
cse Char(4096) const options(*varsize);
counter Int(10) const;
msgKey Char(4) const;
toRemove Char(10) const;
errCd LikeDs(errCode_t) options(*varsize);
cseLen Int(10) const options(*nopass);
cseQualifier LikeDs(callStackEntryQual_t) const options(*nopass);
removeExcept Char(10) const options(*nopass);
cseDataType Char(10) const options(*nopass);
allowRpyReject Char(10) const options(*nopass);
end-pr;
// ------------------------------------
// QMHSNDPM - Send Program Message
// See the Knowledge Center for descriptions of parameters
dcl-pr qmhsndpm ExtPgm('QMHSNDPM');
messageId Char(7) const;
messageFile LikeDs(qualName_t) const;
messageData Char(32767) const options(*varsize);
messageDataLen Int(10) const;
messageType Char(10) const;
cse Char(4096) const options(*varsize);
counter Int(10) const;
msgKey Char(4) const;
errCd LikeDs(errCode_t) options(*varsize);
cseLen Int(10) const options(*nopass);
cseQualifier LikeDs(callStackEntryQual_t) const options(*nopass);
waitTime Int(10) const options(*nopass);
cseDataType Char(10) const options(*nopass);
ccsid Int(10) const options(*nopass);
end-pr;