Wednesday, January 13, 2016

Retrieving the program's name in a CL program

sndpgmmsg rcvmsg

I have always had a standard that whatever output I produce from the IBM i I always put the program's name on it. This is very useful when a user brings me a screen print or report I can quickly go to the program they have a question about. Many years ago I found that some of my colleagues would copy my display and printer file code without bothering to change the name of the program "hard coded" on the file. To overcome this I code the program name as a field in the file, and I always retrieve the name of the program from within the program itself. In RPG the program name is a subfield in the Program Data Structure so it can be easily retrieved, for an example see the post Externally described Data Structures.

Unfortunately there is no equivalent of the Program Data Structure in CL. The program name can be retrieved in a two steps by sending and receiving a program message. There are two versions of my example depending on what I want to retrieve:

  1. Retrieve only the program's name
  2. Retrieve the program's, module's, and procedure's names

Before I go into the example programs I want to describe the part of these example programs that does the retrieving:

06  SNDPGMMSG MSG(' ') TOPGMQ(*SAME) MSGTYPE(*INFO) +
                KEYVAR(&KEYVAR)

07  RCVMSG PGMQ(*SAME) MSGTYPE(*INFO) RMV(*YES) +
             SENDER(&SENDER) SENDERFMT(*LONG)

The first step is to send a message to the current call stack's message queue, using the Send Program Message Command, SNDPGMMSG. The message, MSG parameter, can be anything, I have chosen for it to be blank. I am sending it to the current program message queue, TOPGMQ(*SAME), as an informational message, MSGTYPE(*INFO). I have found that I have to use the KEYVAR parameter, I just use a blank variable.

Having sent a message I need to receive it, when I do I get the program and module name that the message was sent from. Since my program sent the message this will give me the current program's name. I use the Receive Message command, RCVMSG, to get the message and its information. The program queue parameter needs to be for the current program message queue, PGMQ(*SAME), and the message type needs to be the same as the one in the SNDPGMMSG, MSGTYPE(*INFO). I want to remove the message from the message queue once it is retrieved, this is done using RMV(*YES)

The next two parameters of the RCVMSG are where the program's, module's, and procedure's name can be found. The SENDER parameter contains various bits of information about the sender of the message. As this message was sent by the same program it contains information about this program. This information comes in two layouts, depending on what value is in sender format parameter, SENDERFMT. If the short sender format, SENDERFMT(*SHORT), I can retrieve just the program's name. If the long sender format, SENDERFMT(*LONG), I can retrieve the program's, module's, and the procedure's names. Where the program's name is in the SENDER data differs between the two formats.

Sender format Information Length Starting
position
Short Program's name 10 27
Long Program's name 10 42
Module's name 10 54
Procedure's name1 256 64

1 According to the documentation this could contain a list of the nested procedure names'. No matter what I did in testing (RPG procedure calling a CL procedure, one CL procedure calling another) I could only get the procedure name that the SNDPGMMSG was in.

How to get the program's, module's and procedure's name out of &SENDER variable? I could use three Change Variable commands, CHGVAR, each with a substring. But in my opinion that is lines of code and work I do not need. I would use Defined Variables instead, which acts like defining RPG data structure subfields. If you unfamiliar with them you should read Data Structures in CL.

03  DCL VAR(&SENDER) TYPE(*CHAR) LEN(720)

04  DCL VAR(&PROGRAM) TYPE(*CHAR) STG(*DEFINED) +
          LEN(10) DEFVAR(&SENDER 42)

05  DCL VAR(&MODULE) TYPE(*CHAR) STG(*DEFINED) +
          LEN(10) DEFVAR(&SENDER 54)

06  DCL VAR(&PROCEDURE) TYPE(*CHAR) STG(*DEFINED) +
          LEN(256) DEFVAR(&SENDER 64)

In the example above the variables &PROGRAM, &MOPDULE, and &PROCEDURE are "subfields" of SENDER. For example: &PROGRAM is ten long starting at position 42 of &SENDER.

And now on to the example programs…

 

Retrieve only the program's name

Most of the time I just want to display the program's name with a suffix, for example CLPGM-1. For that I can use the short format of the Sender data, which happens to be the command's default value for this parameter.

01  PGM

02  DCL VAR(&KEYVAR) TYPE(*CHAR) LEN(4)
03  DCL VAR(&SENDER) TYPE(*CHAR) LEN(80)

04  DCL VAR(&PROGRAM) TYPE(*CHAR) STG(*DEFINED) +
          LEN(10) DEFVAR(&SENDER 27)

05  SNDPGMMSG MSG(' ') TOPGMQ(*SAME) MSGTYPE(*INFO) +
               KEYVAR(&KEYVAR)

06  RCVMSG PGMQ(*SAME) MSGTYPE(*INFO) RMV(*YES) +
             SENDER(&SENDER)

07  CHGVAR VAR(&PROGRAM) VALUE(&PROGRAM |< '-1')

08  ENDPGM

Line 2: This is the blank parameter I am going to use in the SNDPGMMSG's KEYVAR parameter.

Line 4: In the short format of the Sender's data the program's name is ten long and starts in the 27th position.

Line 5: This is the Send Program Message command, as I have described it above.

Line 6: I only want the short format of the Sender's data. As this is the command default it does not have to be given. The short format is 80 characters, see the definition of the &SENDER variable on line 2.

Line 7: As the variable &PROGRAM is a Defined variable, "subfield", of &SENDER I can use it immediately. Here I am applying the suffix -1 to the name of the program, so the variable will contain the value CLPGM-1.

 

Retrieve the program's, module's, and procedure's names

To be able to get the module's and procedure's name I must use the long format of the Sender's data.

01  PGM

02  DCL VAR(&KEYVAR) TYPE(*CHAR) LEN(4)
03  DCL VAR(&SENDER) TYPE(*CHAR) LEN(720)
04  DCL VAR(&PGMMODPRC) TYPE(*CHAR) LEN(32)

05  DCL VAR(&PROGRAM) TYPE(*CHAR) STG(*DEFINED) +
          LEN(10) DEFVAR(&SENDER 42)
06  DCL VAR(&MODULE) TYPE(*CHAR) STG(*DEFINED) +
          LEN(10) DEFVAR(&SENDER 54)
07  DCL VAR(&PROCEDURE) TYPE(*CHAR) STG(*DEFINED) +
          LEN(256) DEFVAR(&SENDER 64)

08  SNDPGMMSG MSG(' ') TOPGMQ(*SAME) MSGTYPE(*INFO) +
                KEYVAR(&KEYVAR)

09  RCVMSG PGMQ(*SAME) MSGTYPE(*INFO) RMV(*YES) +
             SENDER(&SENDER) SENDERFMT(*LONG)

10  CHGVAR VAR(&PGMMODPRC) VALUE(&PROGRAM |< '/' +
         || &MODULE |< '/' || &PROCEDURE)

11  ENDPGM

Line 2: As I am using the long format of the Sender parameter it has to be defined as 720 character.

Lines 5 - 7: I now have three "subfields" for the program's, module's, and procedure's names. Notice that the program's name starts in a different position, 42, than it did when I used the short format.

Line 9: I need to set the SENDERFMT to *LONG.

Line 10: I can concatenate the three "subfields" together into a new variable to display all three separated by a slash character. In my tests this would display 'TESTRPG/PROCCL/PROCCL'.

 

Personally I only use the program's name, so my programs contain the code shown in the first example.

 

You can learn more about this from the IBM website:

 

This article was written for IBM i 7.2, and should work for earlier releases too.

2 comments:

  1. Great article, as usual. The SNDPGMMSG/RCVMSG combination could also be coded as:

    SNDPGMMSG MSG(' ') TOPGMQ(*SAME) KEYVAR(&MSGKEY)
    RCVMSG MSGKEY(&MSGKEY) SENDER(&SNDINFO) SENDERFMT(*LONG)

    The KEYVAR parameter of the SNDPGMMSG command is actually a return variable which is populated with a unique message identifier after the command is run, which can then be used in the MSGKEY parameter of the RCVMSG command to get the message just sent. Also, the RMV parameter of RCVMSG defaults to *YES, so I didn't include it.

    ReplyDelete
  2. I'm able to retrieve program name even without using KEYVAR.
    SNDPGMMSG MSG(' ') TOPGMQ(*SAME)
    RCVMSG MSGTYPE(*INFO) SENDER(&SENDER)

    Will this cause a problem later?

    ReplyDelete

To prevent "comment spam" all comments are moderated.
Learn about this website's comments policy here.

Some people have reported that they cannot post a comment using certain computers and browsers. If this is you feel free to use the Contact Form to send me the comment and I will post it for you, please include the title of the post so I know which one to post the comment to.