Using IBM i? Need to create Excel, CSV, HTML, JSON, PDF, SPOOL reports? Learn more about the fastest and least expensive tool for the job: SQL iQuery.
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.
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.
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.
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.
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.
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:
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.
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.
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!
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.