Wednesday, December 18, 2013

QCAPCMD another alternative to QCMDEXC

Several of you commented on Capturing QCMDEXC error codes that you preferred to use the C library function SYSTEM( ) or the API QCAPCMD.

I have to confess that I had not used the QCAPCMD API prior to writing this post. So after some playing around with it this is what I found out about using it:

QCAPCMD uses two data structures which I need to explain. The first is called the "Option Control Block", and is used to pass values to the API to control how it behaves. Below is how I coded it in this example.

02 D OptCtlBlock     DS
03 D                               10I 0 inz(0)
04 D                                1    inz('0')
05 D                                1    inz('0')
06 D                                1    inz('0')
07 D                                4    inz(x'00000000')
08 D                               10I 0 inz(0)
09 D                                9    inz(x'000000000000000000')

I have not given the sub-fields names as I am not going to change or use the values of this data structure in any other way except to call QCAPCMD. The sub-fields are:

  1. Type of command processing. '0' (zero) means "command running", or I want to execute the command passed.
  2. DBCS data handling. '0' (zero) means I want to ignore DBCS data.
  3. Prompter action. '0' (zero) no prompting.
  4. Command string syntax. '0' (zero) means I will use the system (IBM i) syntax, rather than System/38 syntax (scary!).
  5. Message retrieve key. This is used during the prompting process. As I am not prompting I need to set this to 8 hexadecimal zeros.
  6. CCSID of command string. '0' (zero) means that I will use the CCSID of the job.
  7. Reserved. Not used but still has to be set to 18 hexadecimal zeros.

The next data structure we have to be concerned with is QUSEC. This is used by several APIs for returning error data. IBM has provided us with a source member for it that we can copy into this program.

10 D/copy qsysinc/qrpglesrc,qusec

If you look in that source member you find that QUSEC has the following sub-fields:

Sub-field Description
QUSBPRV Bytes provided
QUSBAVL Bytes available
QUSEI Exception id (CPF error code)
QUSERVED Reserved (unused)

There is also a data structure called QUSC0200 in the copied source member. I have failed to find what it could be used for in this example.

In this example I am defining QCAPCMD as a procedure, with the EXTPGM keyword, therefore, I can call this program without having to leave free format. This is how I have defined the procedure prototype for it:

11 D QCAPCMD         PR                  ExtPgm('QCAPCMD')
12 D                            32702    options(*varsize) const
13 D                               10I 0 const
14 D   ocb                               like(OptCtlBlock) const
15 D                               10I 0 const
16 D                                8    const
17 D                            32702    options(*varsize) const
18 D                               10I 0 const
19 D                               10I 0
20 D                            32767    options(*varsize)

Yet again I have not given field names to the parameter fields, the exception is ocb as I have used the LIKE keyword I have to give it a name. The parameters are:

  1. Command string.
  2. Length of the command string.
  3. Option control block.
  4. Length of the option control block.
  5. Format of option control block.
  6. Changed command string. Used with prompting, not relevant in this example.
  7. Length available for changed command string. Used with prompting, not relevant in this example.
  8. Length of changed command string available to return. Used with prompting, not relevant in this example.
  9. Error code, data structure QUSEC.

Now we can put it all together into the program:

01 H dftactgrp(*no)

    * Option Control Block
02 D OptCtlBlock     DS
03 D                               10I 0 inz(0)
04 D                                1    inz('0')
05 D                                1    inz('0')
06 D                                1    inz('0')
07 D                                4    inz(x'00000000')
08 D                               10I 0 inz(0)
09 D                                9    inz(x'000000000000000000')

    * Error code parameter
10 D/copy qsysinc/qrpglesrc,qusec

    * Procedure definition for program QCAPCMD
11 D QCAPCMD         PR                  ExtPgm('QCAPCMD')
12 D                            32702    options(*varsize) const
13 D                               10I 0 const
14 D   ocb                               like(OptCtlBlock) const
15 D                               10I 0 const
16 D                                8    const
17 D                            32702    options(*varsize) const
18 D                               10I 0 const
19 D                               10I 0
20 D                            32767    options(*varsize)

21 D Command         S           1000    varying
22 D ChgdCommand     S          32702    varying
23 D Unused          S             10I 0
    /free
24     Command = 'CLRPFM QTEMP/TESTFILE' ;

25     QCAPCMD(Command:
               %len(Command):
               OptCtlBlock:
               %size(OptCtlBlock):
               'CPOP0100':
               ChgdCommand:
               %len(ChgdCommand):
               Unused:
               QUSEC) ;

26     if (QUSEI <> ' ') ;
          .
          .
27     endif ;

28     *inlr = *on ;

The only part that is left to explain is the call of the procedure QCAPCMD, line 25. I have put each parameter on a seperate line to make it easier to understand. The parameters are:

  1. Command = Command string to clear the file QTEMP/TESTFILE.
  2. %len(Command) = Length of the string in the Command field.
  3. OpCtlBlock = Option control block data structure.
  4. %size(OptCtlBlock) Size of the option control block data structure.
  5. 'CPOP0100' = Format of option control block (C-P-O-P-zero-1-zero-zero).
  6. ChgdCommand = Not used, blank.
  7. %len(ChgdCommand) = As ChgdCommand is blank then this is zero.
  8. Unused = Not used, zero.
  9. QUSEC = Error code returned in the QUSEC data structure.

The built in functions %LEN and %SIZE are not the same. %LEN returns the length of the string in the field, %SIZE returns the total length. If we use these two built in functions on the Command field in the program:

   x = %len(Command) ; 
   y = %size(Command) ;

The results would be: x = 21 and y = 1002. y is 1002 as it is the length of the Command field, 1000 bytes, and 2 additional bytes as the field was defined as variable, varying.

Back to the program, on line 26 I check if the QUSEC sub-field QUSEI is not blank, if it is not then the command produced an error.

 

You can learn more about these from the IBM web site:

 

This article was written for IBM i 7.1, and it should work with earlier releases too.

 

Conclusion

Of the three methods I have described in this series of excuting CL commands in RPGLE programs, QCMDEXC, SYSTEM( ), or QCAPCMD I still prefer to use QCMDEXC with a MONITOR group as the easiest method to code and for others to understand.

What do you think? Add your thoughts below in the Comments secition of this post.

8 comments:

  1. >> There is also a data structure called QUSC0200 in the copied
    >> source member. I have failed to find what it could be used for
    >> in this example.

    Just a FYI - the API Error Code structure can be longer than the structure defined by QUSEC (see Infocenter link below). If a longer structure is defined and the first "Bytes provided" field is set to the structure length, when an error occurs, the exception/message data for the error is stored, starting at offset 16. In some cases the exception data might be useful to the application executing the CL command.

    Another idea: if using ILE, a common module/routine (eg in a *SRVPGM) could be created to "wrap" this API, with a simple calling linkage that hides much of this API's complexity.

    http://pic.dhe.ibm.com/infocenter/iseries/v7r1m0/index.jsp?topic=%2Fapiref%2Ferrorcodeformat.htm

    ReplyDelete
  2. I recently discovered with 7.1 you do not need BndDir( 'QC2LE' ) in your “H” spec. for calling the Extproc command.

    ReplyDelete
    Replies
    1. That has been true since V6.1 Andrew.

      Delete
  3. We all lack the time to play around with the system and investigate useful or new features, therefor I appreciate exchanging ideas like provided in the thread.

    ReplyDelete
  4. S├ębastien JulliandJanuary 3, 2014 at 6:17 AM

    Be careful when including QUSEC from QSYSINC to use it "as is". The Bytes Provided field (QUSBPRV) must be initialized with the size of the QUSEC data structure.

    This should do the trick
    Clear QUSEC;
    QUSBPRV = %Size(QUSEC);

    A good idea is to redefine QUSEC in a /Copy of your own, using the INZ option to make sure each field is OK:

    D QUSEC DS
    D QUSBPRV 10I 0 Inz(%Size(QUSEC))
    D QUSBAVL 10I 0 Inz(0)
    D QUSEI 7A Inz(*Blank)
    D QUSERVED 1A Inz(*Blank)

    Moreover, the RESET BIF can...reset...the DS, with the correct values, for further use in the program.

    ReplyDelete
  5. I Think QCMDEXE is the easiest way to call CL commands within RPG... This API has a lot of parameters and need a lot of coding....

    ReplyDelete
  6. Which is why most who use it wrap it in a subprocedure so that is ends up being every bit as as easy to use as QCMDEXE. In fact it can be simpler since the subproc can return as value allowing the success/failure status to be returned as a variable just as system() does. Actually I also wrap up QCMDEXE the same way.

    ReplyDelete
  7. Re Sebastien's QUSEC comment, I'd like to add that the Error Code structure is common to many other APIs as well. In addition to initialization, the "Bytes available" field can be tested after the API call, to determine if the API call encountered an error. Zero means no error; greater than zero means an error occurred. This assuming "Bytes provided" field is initialized to the size of the Error Code structure prior to the API call.

    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.