Part of the iSeries API is a rich collection of program routines that provide access to processing services not otherwise available. These are IBM C/400 function routines that can perform just about anything the user is allowed to do. These library functions give the ILE RPG programmer access to system functions that can get into areas like communications, security, and low-level data functions.
ILE RPG to the Rescue
With the advent of ILE technology for the iSeries, any ILE programming language has the capacity to interface with API routines that were originally intended for use only with C/400. It takes a bit of coding to create an interface to a C/400 API in an RPG program, but the rules generally apply for all the C-style APIs. When you've coded one, you've coded them all.
The iSeries C/400 Functions Library
The low-level routines that make up the iSeries API come it two varieties: the kind you call from your code and the kind you include in your code. The kind you call are like any program called from within another program; the Call opcode is used, and the program is passed parameters, as shown in this call to a security API named QSYGETPH:
C Eval #Handle = *Allx'00'
C Call 'QSYGETPH' 77
C Parm #UserID
C Parm #PassWord
C Parm #Handle
The other type of API functions are in the form of C/400 library functions. As such, they're not called from your program but instead are included with your program's object code. To make this happen, IBM has provided the integration mechanisms to bring RPG and C/400 together.
The first task is to identify the function you need. IBM's API Finder Web site (for callable APIs) has made the job of finding the right API for the task at hand a bit easier. The general listing of available API documentation is at IBM's APIs by Category Web page. At these sites, you can select from dozens of API categories to narrow your search, or if you know the name of the API in question, you may display its documentation directly.
ILE RPG Procedures
ILE RPG programs can include functions from the C/400 library, but the correct interface must be created within the RPG code. The interface is accomplished with ILE RPG's support for static procedure calls.
Using a static procedure call in an RPG program consists of three steps:
- Create a procedure prototype in the D-specs.
- Define variables and structures in your program that will be correctly passed to the procedure as arguments.
- Execute the procedure by using the procedure name in an Eval expression.
Create a Procedure Prototype
For example, suppose you want to write your own TCP/IP communications application--a Web server, let's say. To do that, you have to use system-level TCP/IP functions provided by IBM. This illustration uses two of the TCP/IP functions--"socket" and "bind"--to show how C/400 functions are accessed.
A procedure prototype is the specification that allows the ILE RPG compiler to create a static link to an external procedure. You add D-specs in your program to define the interface prototype, as shown below:
D MySocket PR 9b 0 ExtProc('socket') (A)
D SockFam 9b 0 Value (B)
D AddrType 9b 0 Value
D Protocol 9b 0 Value
The line of code designated by (A) declares a prototype named MySocket that uses an external routine, the IBM function named "socket." Note the "PR" designation that identifies this structure as a prototype and the ExtProc keyword that identifies the name "socket" as that of an external routine. The socket API routine accepts three parameters (B): the socket family, the IP address type, and the protocol to use (TCP in this case). Each of the three parameters is a C-style integer and is coded in RPG as a binary field of nine digits and zero decimal positions.
The socket API routine is a function, meaning that it may be used in an expression (with an Eval opcode) and it returns a value. Therefore, the type of data returned must also be declared. That's why the statement at (A) is assigned a field type (also nine binary digits with zero post-decimal positions.)
Define Variables and Structures as Arguments to an API Routine
Other prototype structures consist of more-complex data elements and, in certain cases, a pointer to a variable or structure. For example, consider the prototype structure for the "bind" API routine:
D MyBind PR 9b 0 ExtProc('bind')
D BindSock 9b 0 Value
D BindSA * Value (C)
D BindSize 9b 0 Value (D)
This prototype structure is established to allow the RPG program to interface with the "bind" sockets API routine. Notice the parameter line at (C). The BindSA parameter in the bind routine is a pointer to a structure. A pointer is really a value that represents the address, within internal iSeries memory, where the first byte of the structure resides. The structure itself is also defined in the D-specs as a data structure:
D ServerAddr DS
D sin_family 5i 0 Inz(0)
D sin_port 5u 0 Inz(0)
D sin_addr 10u 0 Inz(0)
D sin_zero 1a Dim(8) Inz(X'00') (E)
API routines written for C often accept a structure as an argument in this manner. The pointer defined in the prototype structure refers to the memory address of the first byte of a structure like ServerAddr. To further complicate the picture, the fourth field of the structure in the code shown above at (E) is an array.
Frequently when passing a structure, you'll have to also pass a numeric argument that contains the length of the structure. This "length" argument is used by the C API routine to know the dimension of the structure. This is the case with the bind routine I showed. The argument prototyped at (D) represents the length of the structure prototyped as the pointer at (C).
Execute a Procedure by Using the Procedure Name in an Eval Expression
The next step in this process is to prepare the arguments. You do so by loading the variables with the appropriate values, as in the code below at (F) through (I). In this example, AF_INET at (F) and INADDR_ANY at (G) are constants representing simple numeric values. #Port at (H) is an ordinary variable that contains a numeric value (like 80 for our Web server).
At line (I), the program variable #iWork is being loaded with the length of the structure ServerAddr to be passed as a argument. The built-in function %Size is used to get the length of ServerAddr.
To actually execute an API routine of this type, you reference the name you used in the prototype in an Eval expression (J).
You recall that the bind routine is a function and, as such, will return a value. At (J) in the example below, the value returned by executing the bind routine will be placed in the program variable "dummy."
C Eval sin_family = AF_INET (F)
C Eval sin_addr = INADDR_ANY (G)
C Eval sin_port = #Port (H)
C Eval #iWork = %Size(ServerAddr) (I)
C Eval dummy = MyBind(#Socket: (J)
C %Addr(ServerAddr): (K)
C #iWork)
The argument passed at line (K) uses the built-in RPG function %Addr to determine the address pointer of the first byte of the ServerAddr structure. The pointer, then, is passed to the external routine, along with the length of the structure.
When the RPG source code is compiled, the system will locate the C/400 functions that are referenced in the procedure prototypes and include them with your created object code.
Why Wait?
iSeries C/400 functions are powerful, fast and, in many cases, the only way to perform system functions from within an application program. By becoming familiar with the C/400 library functions and the way they're used in an ILE RPG program, you can have a high-level perspective and low-level access all in the same package.
Chris Peters has 26 years of experience in the IBM midrange and PC platforms. Chris is president of Evergreen Interactive Systems, a software development firm and creators of the iSeries Report Downloader. Chris is the author of The OS/400 and Microsoft Office 2000 Integration Handbook, The AS/400 TCP/IP Handbook, AS/400 Client/Server Programming with Visual Basic, and Peer Networking on the AS/400 (MC Press). He is also a nationally recognized seminar instructor. Chris can be reached at
LATEST COMMENTS
MC Press Online