Midrange News for the IBM i Community


Change Program Information (CHGPGMINF) Command Published by: Bob Cozzi on 03 May 2011 view comments
© 2011 Robert Cozzi, Jr. All rights reserved.

© Robert Cozzi, Jr. All rights reserved. Reproduction/Redistribution prohibited.
A midrangeNews.com Publication

RPG TnT Reprinted, 5250 KeepAlive Solved and Setting a Program's Creation Source Member

Eureka! I Have Found It

In one of my other columns ("Bob's BLOG")  published by MidrangeNews.com, I write about fixing the Windows 7 TimeOut issue that occurs with IBM iSeries Access and RDi/RDp. A few weeks after running the free utility included in that article, I have not experienced a single disconnect from the IBM i host system. If you have moved to or are considering moving to Windows 7 (or Vista) and you use RDi/RDp or Client/iSeries Access (yes, both names are embedded in the code) you should read that blog entry and download the free utility called SetKeepAlive that I wrote for that article. This program runs on Windows Vista and Window 7 and allows you to create a timeout/retry (aka "Keep Alive") entry in the Windows Registry. You can create the Registry Entry yourself, with RegEdit, but SetKeepAlive really makes it easy when you have multiple systems or just want to get it done. You only have to run it once on your PC and then you can unstall it if you desire. In addition to the Registry, you may optionally and automatically update the iSeries Access .WS files that exist on your PC. SetKeepAlive will insert the necessary entry to insure that iSeries Access uses the Keep Alive setting you specified for the Registry.

Sponsored by: BCD Presto 3

Since I ran SetKeepAlive on the two Windows 7 PCs that I use, I have not had a single disconnection issue with RDp nor any of the programs bundled with iSeries Access. Have a look at the blog entry here, and if you need it, download the SetkeepAlive program that I wrote, the link is near the end of the article. 

Bob Cozzi's "101 RPG Tips 'n Techniques" Now Available

While my former publisher seemed to hide "RPG TnT: 101 Dynamite Tips 'n Techniques" from most of you (yes I heard from you!) I am excited to announce that the transfer of this book to a new publisher has been completed. I expect good things from MCPressOnline.com as they have been successfully handling my "Modern RPG IV" books for over a decade. You can order your copy of "RPG TnT" (its short name) directly from the MCPress website. Soon you will also be able to order from Amazon.com, in the meantime, you can read the Amazon book reviews here, and then jump over to MCPressOnline.com to place your order today.

What Source Created My Program?

One concept behind the Integrated Language Environment or ILE is that you can create program objects from one or more source members. This capability is at least 17 years old as of this writing, is widely used, and yet has not become a standard. What I mean is that while many (most?) shops have used the multi-module-program capabilities of the ILE compilers, it is still not standard practice for most applications. So you may have a few multi-module *PGM objects in your shop, but the majority of your applications are likely to be single-module programs. That is, you use PDM option 14 to compile most of your stuff, or more accurately, we still program as if we are using RPGIII.

Not that there isn't any call for single-module programs, there is. In fact, small utilities or one-off quick-n-dirty routines are often easier to build using a single module program.

One of the problems with a single-module ILE program is that the DSPOBJD (display object description) command no longer displays the source code used to "create the program". Take a Look at the following DSPOBJD screen capture from a single-module program I created for this article.

                         Display Object Description - Service    

 Object . . . . . . . . . . . . . . . :   CHGPGMINF     
   Library  . . . . . . . . . . . . . :     RPGREPORT   
 Library ASP device . . . . . . . . . :   *SYSBAS 
 Library ASP group  . . . . . . . . . :   *SYSBAS 
 Type . . . . . . . . . . . . . . . . :   *PGM    
  
 Source file  . . . . . . . . . . . . :     
   Library  . . . . . . . . . . . . . :     
 Member . . . . . . . . . . . . . . . :     
 Attribute  . . . . . . . . . . . . . :   RPGLE   
 User-defined attribute . . . . . . . :     
 Freed  . . . . . . . . . . . . . . . :   NO
 Size . . . . . . . . . . . . . . . . :   188416  
 Creation date/time . . . . . . . . . :   01/27/11  12:07:38  
 Source file date/time  . . . . . . . :     
 System level . . . . . . . . . . . . :   V7R1M0  
 Compiler . . . . . . . . . . . . . . :   CRTPGM     V7R1M0   
                                                                 More... 
 Press Enter to continue. 
  
 F3=Exit   F12=Cancel     
 (C) COPYRIGHT IBM CORP. 1980, 2009.  

Notice the Source file and Library entries; they are blank. When we moved to RPG IV from RPGIII we also moved from a compiler that targeted the original CPF program model, to one that supports an integrated program module. That program model, known as the Integrated language environment or ILE is what allows object code produced by RPG IV, C, C++, ILE CL, and COBOL to target the same structure. This allows us to create a program that is comprised of (for example) RPG IV, C++ and even CL object code. This is a nice feature because it allows us to write subprocedures in the "correct" language for the task and expose it for use by RPG IV programmers. A subprocedure written in C may be exposed to RPG IV by prototyping it in RPG IV. Then the RPG IV and C *MODULE objects are combined to create the final *PGM object. Pretty cool, huh!

But thinking about the situation illustrated in the Display Object Description output, above, what did we loose by moving to ILE? We lost the integrated reference to the source code used to create the program. That is the association of the original source code used to in producing the program object. Why did this happen? Because ILE program objects are NOT created from source code. They are in fact, created by combining or "binding" as it is called, one or more *MODULE objects together with some loader code that turns them into a *PGM object. It doesn't make sense that there would be a "created by" source member name associated with the program object, because they aren't directly created from source.

The problem is this, many RPG developers continue to associate a single source file member with a program object. While this may seem logical, it is more of a habit than a technically accurate assumption. IBM has provided an alternative solution for single- and multi-module programs; the DSPPGM command.

Display Program Information

IBM added the DSPPGM (Display Program Information) command that, as the name implies, displays all the information about a program object. The save/restore information available from DSPOBJD is not, however included with DSPPGM, but a lot of other important information is included. Here is a sample page generated by DSPPGM:

                          Display Program Information
                                                                 Display 1 of 7
 Program  . . . . . . . :   CHGPGMINF     Library  . . . . . . . :   RPGREPORT 
 Owner  . . . . . . . . :   COZZI                                              
 Program attribute  . . :   RPGLE                                              
 Detail . . . . . . . . :   *BASIC                                             
                                                                               
                                                                               
 Program creation information:                                                 
   Program creation date/time . . . . . . . . . . :   01/27/11  12:07:38   
   Type of program  . . . . . . . . . . . . . . . :   ILE                      
   Program entry procedure module . . . . . . . . :   CHGPGMINF                
     Library  . . . . . . . . . . . . . . . . . . :     QTEMP                  
   Activation group attribute . . . . . . . . . . :   QILE                     
   Shared activation group  . . . . . . . . . . . :   *NO                      
   User profile . . . . . . . . . . . . . . . . . :   *USER                    
   Use adopted authority  . . . . . . . . . . . . :   *YES                     
   Coded character set identifier . . . . . . . . :   65535                    
   Number of modules  . . . . . . . . . . . . . . :   1                        
                                                                        More...
 Press Enter to continue.                                                      
                                                                               
 F3=Exit   F12=Cancel                                                          

There are 7 pages of information presented with DSPPGM. The basic program attributes are listed on the Display Program Information panels. The basic program attributes listed on the first page includes the entry module, activation group, object owner, and the adopt authority attributes. Although not an official declaration, typically if the Entry Procedure Module is from QTEMP, it often means that PDM option 14 (CRTBNDRPG) was used to create the program.

Page 3 of DSPPGM contains the list of modules that were used to create the program. Obviously if a one-module program is being display, just one module is listed. Using the example program CHGPGMINF, that I created for this article, it would look like the following:

                          Display Program Information                           
                                                                 Display 3 of 7 
 Program  . . . . . . . :   CHGPGMINF     Library  . . . . . . . :   RPGREPORT  
 Owner  . . . . . . . . :   COZZI                                               
 Program attribute  . . :   RPGLE                                               
 Detail . . . . . . . . :   *MODULE                                             
                                                                                
                                                                                
 Type options, press Enter.                                                     
   5=Display description   6=Print description                                  
                                                                                
                                          Creation  Optimization  Debug         
 Opt  Module      Library     Attribute   Date         Level      Data          
  _   CHGPGMINF   QTEMP       RPGLE       04/25/11  *NONE         *YES          
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                         Bottom 
 F3=Exit   F12=Cancel   F17=Top   F18=Bottom                                    

From this page, you may type in option 5 to display the specific description of a module. It is on that page where the creating-source member name is listed. Here is what that module information subpage looks like:

                          Display Program Information                          
                                                                               
 Program  . . . . . . . :   CHGPGMINF     Library  . . . . . . . :   RPGREPORT 
                                                                               
 Module attributes:                                                            
   Module . . . . . . . . . . . . . . . . . . . . :   CHGPGMINF                
     Library  . . . . . . . . . . . . . . . . . . :     QTEMP                  
   Source file  . . . . . . . . . . . . . . . . . :   QRPGLESRC                
     Library  . . . . . . . . . . . . . . . . . . :     RPGREPORT              
   Source member  . . . . . . . . . . . . . . . . :   CHGPGMINF                
   Module attribute . . . . . . . . . . . . . . . :   RPGLE                    
   Module creation date/time  . . . . . . . . . . :   01/27/11  12:07:38   
   Source file change date/time . . . . . . . . . :   01/27/11  12:02:14       
   Coded character set identifier . . . . . . . . :   37                       
   Creation data  . . . . . . . . . . . . . . . . :   *YES                     
   Allow RTVCLSRC (CL module) . . . . . . . . . . :   *NO                      
   Sort sequence table  . . . . . . . . . . . . . :   *HEX                     
   Language identifier  . . . . . . . . . . . . . :   *JOBRUN                  
   Optimization level . . . . . . . . . . . . . . :   *NONE                    
                                                                        More...
 Press Enter to continue.                                                      
                                                                               
 F3=Exit   F12=Cancel                                                          

On this page, you see the source file, library and member names used to create the module. If you prefer, you may control the DSPPGM command and display this page directly by specifying the DETAIL(*MODULE) parameter.

When a multi-module program is being displayed with DSPPGM, each module needs to be displayed independently to see the list of source members used to create the modules used by the program. This is why the DSPOBJD command no longer displays source for ILE-based programs.

When a one-module program is being displayed, the module's source file member name is obviously the name of the source file member used to create the program. Note the module name is from QTEMP. When CRTBNDRPG is used, a temporary module is created in QTEMP and then the CRTPGM command is called "under the covers". So a program identified as being created from a module in QTEMP usually means the CRTBNDRPG command (or PDM option 14) was used to create it.

So that solves the "I can't see the source member used to create my RPG IV program" issue, doesn't it? Partially. What it does not do is provide a solution for the "recompile-the-world" tools that rely on the output from the DSPOBJD command. If the DSPOBJD output has a blank source file member name, it creates a problem for these types of tools.

Certainly there are other issues when a multi-module program enters a "recompile-the-world" tool. For that situation, a manually controlled solution, or a CL "build" program is often the best choice to recreate the program; and there are RDp options as well.

But how can we accommodate a recompile tool that was probably written for RPGIII architecture and the old OS/400 or perhaps even CPF--how can we make it work with single-module RPG IV programs? You either have to create a database cross reference of modules to programs/service programs and use it instead of DSPOBJD output or you can use the CHGPGMINF (change program information) command that I wrote for this article.

Creating a new database cross reference of modules, a "module where used" database if you will, is something I'll cover in a future RPG Report. But today I want to go over the CHGPGMINF command.  

Change Program Information (CHGPGMINF) Command

I wrote a sample Command that will change the source file member name for a *PGM object--even if that *PGM object an ILE object created from one or more modules. This allows those "recompile-the-world" tools that I mentioned earlier, to continue to "work" with single-module ILE programs. The example CHGPGMINF command does the following:

  • Extracts the name of the entry module of a *PGM object
  • Retrieves the corresponding source file member name of the entry module
  • Changes the *PGM object's source file member name from blanks to the entry module's source member name

After using CHGPGMINF, if you run DSPOBJD on the program object, the output should contain the entry module's source file member name as the program's source member name.

The Command Definition Source code for CHGPGMINF follows:

 CHGPGMINF:  CMD        PROMPT('Change Program Information')
             /*         Command processing program is: CHGPGMINF  */
             /*  Updates a program's Creation Source File & Mbr   */
             /*  with that of the *MODULE in the *PGM with the    */
             /*  same name as the *PGM.                           */

             PARM       KWD(PGM) TYPE(QUAL2) MIN(1) PROMPT('Program +
                          name')
 QUAL2:      QUAL       TYPE(*NAME) EXPR(*YES)
             QUAL       TYPE(*NAME) DFT(*LIBL) SPCVAL((*LIBL) +
                          (*CURLIB)) EXPR(*YES) PROMPT('Library') 

This command accepts one parameter, PGM, the program name. This program's OID (object information repository) is changed to match that of the source file member from a module in the program with the same name as the program.

The command processing program is written entirely in RPG IV as a single source member (to lend itself to a self-test). It requires only the IBM Openness Includes (Operating System Installation option 13) in order to be compiled. Normally RPG xTools or the free RPG Open library are required. But I wanted to make an all-in-one source member in order to illustrate the one-module program.

Sponsored by: RPG xTools

Enjoy Programming Again! RPG xTools is a collection of subprocedures written by RPG IV author Bob Cozzi. Just add the binding directory and a /COPY statement and you'll have access to more than 200 RPG IV and CGI (that's right Web) subprocedures. Everything from converting a database record to CSV format within RPG, to uploading a photo from a webpage--it all works, time-tested.

Get a 30-day free, no obligation trial of RPG xTools here.

To compile the CHGPGMINF RPG IV source code, using PDM option 14 or the CRTBNDRPG command.

.....H BNDDIR('QC2LE')
     H DFTACTGRP(*NO) OPTION(*SRCSTMT : *NODEBUGIO)

      /INCLUDE QSYSINC/QRPGLESRC,qbnlpgmi
      /INCLUDE QSYSINC/QRPGLESRC,qusgen
      /INCLUDE QSYSINC/QRPGLESRC,qusec

     D apiError        DS                  LikeDS(QUSEC) Inz

     D rtnLibrary      S             10A

     D szModuleList    DS                  Qualified
     D  name                         10A   Inz('MODLIST')
     D  library                      10A   Inz('QTEMP')

     D nPos            S             10I 0

     D apiFmt          C                   Const('PGML0100')
     D QUALOBJ         DS                  Qualified
     D  object                       10A
     D  obj                          10A   overlay(object)
     D  objName                      10A   overlay(object)
     D  name                         10A   overlay(object)
     D  library                      10A
     D  lib                          10A   overlay(library)
     D  libname                      10A   overlay(library)
     D  objlib                       10A   overlay(library)

     D module_T        DS                  Qualified
     D  pgm                                LikeDS(qualObj)
     D  module                       10A
     D  modname                      10A   Overlay(module)
     D  name                         10A   Overlay(module)
     D  library                      10A
     D  modLib                       10A   Overlay(library)
     D  srcfile                            LikeDS(qualObj)
     D  srcMbr                       10A
     D  seuType                      10A

     D module          DS                  LikeDS(module_T)

      *********************************************************
      ** C R E A T E  U S E R  S P A C E
      *********************************************************
     D QusCreateUserSpace...
     D                 PR                  ExtPgm('QUSCRTUS')
     D  userSpaceName                20A   Const
     D  extendedAttr                 10A   Const
     D  nSize                        10I 0 Const
     D  InitValue                     1A   Const
     D  PubAuth                      10A   Const
     D  szTextDesc                   50A   Const
     D  Replace                      10A   Const
     D  api_error                          LikeDS(QUSEC)
     D                                      OPTIONS(*VARSIZE : *NOPASS)
     D  bSysDomain                   10A   Const OPTIONS(*NOPASS)

      *********************************************************
      ** C H A N G E  U S E R  S P A C E  A T T R I B U T E S
      *********************************************************
     D userSpaceAttr   DS                  Qualified
     D  keyCount                     10I 0 Inz(1)
     D  autoExtend                         LikeDS(key_chgus_AutoExt)
     D                                     Inz(*LIKEDS)

          // Set User Space Size
     D key_chgus_Size...
     D                 DS                  Qualified
     D  key                          10I 0 Inz(1)
     D  length                       10I 0 Inz(%size(key_chgus_size.data))
     D  data                         10I 0 Inz

          // Change Initial Value
     D key_chgus_Inz...
     D                 DS                  Qualified
     D  key                          10I 0 Inz(2)
     D  length                       10I 0 Inz(%size(key_chgus_inz.data))
     D  data                          1A   Inz(X'00')

          // Set Auto-Extend Attribute
     D key_chgus_AutoExt...
     D                 DS                  Qualified
     D  key                          10I 0 Inz(3)
     D  length                       10I 0 Inz(%size(key_chgus_autoExt.data))
     D  data                          1A   Inz(*ON)

     D QusChangeUserSpaceAttribute...
     D                 PR                  extPgm('QUSCUSAT')
     D  rtnLibName                   10A
     D  userSpaceName                20A   Const
     D  attribute                          LikeDS(userSpaceAttr)
     D  api_error                          LikeDS(QUSEC) OPTIONS(*VARSIZE)

      *********************************************************
      ** R E T R I E V E  U S E R  S P A C E   D A T A
      *********************************************************
     D QusRtvUS        PR                  extPgm('QUSRTVUS')
     D  szUserSpace                  20A   Const
     D  nStart                       10I 0 Const
     D  nLength                      10I 0 Const
     D  szRtnData                 65535A   Options(*VARSIZE)
     D  api_error                          LikeDS(QUSEC)
     D                                     OPTIONS(*VARSIZE:*NOPASS)

     D ListModules     PR                  extPgm('QBNLPGMI')
     D  userSpace                    20A   Const
     D  Format                        8A   Const
     D  pgmName                      20A   Const
     D  apiError                           LikeDS(QUSEC)
     D                                     OPTIONS(*VARSIZE)

     D GetNextModule   PR            10I 0
     D  module                             LikeDS(Module_T)
     D  ref                          10I 0

     D ChgPgmSrc       PR
     D  pgm                                LikeDS(QUALOBJ) Const
     D  srcFile                            LikeDS(QUALOBJ) Const
     D  srcMbr                       10A   Const OPTIONS(*NOPASS)

     D chgpgmInf       PR                  EXTPGM('CHGPGMINF')
     D  pgm                                LikeDS(qualobj)

     D ChgpgmInf       PI
     D  pgm                                LikeDS(qualobj)
     C                   eval      *INLR = *ON
      /free
         qusCreateUserSpace( szModuleList : 'COZZI_UTILS' :
                              65535 : X'00' : '*ALL' : 'Module List' :
                              '*YES' : apiError);
         userSpaceAttr.autoExtend.data = *ON;
         qusChangeUserSpaceAttribute(rtnLibrary : szModuleList :
                                       userSpaceAttr : apiError );

          ListModules(szModuleList : apiFmt : pgm: apiError);

          dow GetNextModule(module : nPos ) > 0;
             if (module.Name = pgm.name);
               chgpgmsrc(pgm : module.srcfile : module.srcMbr);
             endif;
          enddo;
      /END-FREE

     P GetNextModule   B
     D GetNextModule   PI            10I 0
     D  module                             LikeDS(Module_T)
     D  nRef                         10I 0

     D nRtvLen         S             10I 0
     D nStart          S             10I 0

     D genHdr          DS                  LikeDS(QUSH0100) Inz
     D moduleInfo      DS                  LikeDS(QBNL0100)

      /FREE
           QusRTVUS(szModuleList: 1 : %size(genHdr) : genHdr);

           if (nRef <= 0);
             nRef = 1;
           endif;

           if (nRef > genHdr.QUSNBRLE);
             return 0;
           endif;

          //  Each entry is located at start-of-list, plus length of entry,
          //  (QUSSEE) multipled by the nRefPos (entry number).
           nStart = (genHdr.QUSOLD+1) + ((nRef-1) * genHdr.QUSSEE);
           nRtvLen = %size(moduleInfo);
           QusRTVUS(szModuleList: nStart : nRtvLen : moduleInfo);
            module.Pgm.Name = moduleInfo.QBNPGMN00;
            module.Pgm.Library  = moduleInfo.QBNPGMLN;
            module.Name = moduleInfo.QBNBMN;
            module.library  = moduleInfo.QBNBMLN;
            module.srcfile.name = moduleInfo.QBNSFILN;
            module.srcfile.library = moduleInfo.QBNSFLN;
            module.srcMbr = moduleInfo.QBNSFILM;
            module.seuType  = moduleInfo.QBNMA;

           nRef += 1;

           return nRef;
      /end-free

     P GetNextModule   E


     P ChgPgmSrc       B
     D ChgPgmSrc       PI
     D  pgm                                LikeDS(QUALOBJ) Const
     D  srcFile                            LikeDS(QUALOBJ) Const
     D  srcMbr                       10A   Const OPTIONS(*NOPASS)

     D  OIR_srcInfo_T  DS                  Qualified
     D   srcFile                     10A
     D   srcLib                      10A
     D   srcMbr                      10A

     D keyInfo_T       DS                  Qualified Inz
     D  keyID                        10I 0
     D  length                       10I 0
     D   data                              LikeDS(OIR_srcInfo_T)

     D objInfo_T       DS                  Qualified
     D  keyCount                     10I 0
     D  keyData                            LikeDS(keyInfo_T) Inz(*LIKEDS)


     D QLICOBJD        PR                  EXTPGM('QLICOBJD')
     D  rtnLib                       10A
     D  object                             Const LikeDS(QualObj)
     D  objType                      10A   Const
     D  chgData                    4096A   Const OPTIONS(*VARSIZE)
     D  apiErrorDS                   16A   OPTIONS(*VARSIZE)

     D rtnLib          S             10A

     D myObjInfo       DS                  LikeDS(objInfo_T) Inz

     C                   eval      *INLR = *ON
      /free
            myObjInfo.keyCount = 1;
            myObjInfo.keyData.length = %size(oir_srcInfo_T);
            myObjInfo.keyData.keyID = 1; // Change source lib/file/mbr

            myObjInfo.keyData.data.srcFile = srcfile.name;
            myObjInfo.keyData.data.srcLib  = srcfile.lib;
            if (%parms < 3 or srcmbr = '*PGM' or srcmbr = ' ');
                myObjInfo.keyData.data.srcMbr  = pgm.name;
            else;
                myObjInfo.keyData.data.srcMbr  = srcmbr;
            endif;

           qlicobjd(rtnLib : pgm :'*PGM': myObjInfo : apiError);
           return;

      /end-free
     P chgPgmSrc       E 

The command uses a boatload of APIs and I've prototypes the ones I needed for this example inside the source member body itself, Normally this a taboo as all prototypes should be stored in a /COPY source member. But again, this is just an example.

As we move to more and more multi-module based programs (and service programs) this utility becomes less useful. What will need to be created is either a more robust rebuild utility; one that works with multi-module applications, or a generalized build utility, will need to be integrated into RDp. It's not rocket science to create one of these and the IBM and Open Source tools are shipped, installed, and integrated with the RPG IV developer's workflow... today. But we'll see what happens next.

You're welcome!

Call Me

Bob Cozzi accepts your questions for use in future RPG Report articles or content for midrangeNews.com. Topics of interest include: RPG IV, Web development with RPG IV, APIs, C/C++ or anything else IBM i development related (except subfiles, data areas and RPGII/III because Bob doesn't care about that stuff) write your questions using the Feedback link on the midrangeNews.com website. 

You can subscribe to RPG Report (we call it "follow") by visiting the RPG Report page on midrangeNews.com and then click the FOLLOW link in the table of contents for that page. To unsubscribe, simply click that same link. You must be signed up and signed in to midrangeNews.com to start following RPG Report.

Follow Bob Cozzi on Twitter

Return to midrangenews.com home page.
Sort Ascend | Descend

COMMENTS