RPGIV @ Work

A unique site for RPG and System i Lovers

Welcome!

Hi, this site will provide all what you need in System i and RPG developments.

My Name is Chamara Withanachchi, System i Expert and RPG Developer. And in the field for last 11 years.

I hope you will find lot of valuable information from this site

Calling IBM iSeries RPG Programs with API Calls Print E-mail
User Rating: / 5
PoorBest 
Written by Chamara Withanachchi   

The first thing you will need to do is make sure you have the latest version of IBM Client Access (V5R3 or later) installed an make sure that you install the optional programmers toolkit.


Next, you will need to add a reference to the CWBX library from your application. You can do this from the project menu and select "Add Reference". You need to browse to "C:\Program Files\IBM\Client Access\Shared\cwbx.dll". This will give you access to the IBM API's within the cwbx namespace. You can add cwbx to you using statements if you wish.


You need a program that accepts parameters in and out.


I'm going to create a console application which calls an RPG program and returns a value to the user. This example is very basic and does not perform much in the way of error handling.


using System;
using System.Collections.Generic;
using System.Text;
using cwbx;
 
namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            string result = string.Empty;
 
            StringConverter stringConverter = new StringConverterClass();
 
            // Define an AS400 system and connect to it
            AS400System system = new AS400System();
            system.Define("AS400");
            system.UserID = "USERNAME";
            system.Password = "PASSWORD";
            system.IPAddress = "127.0.0.1";
            system.Connect(cwbcoServiceEnum.cwbcoServiceRemoteCmd);
 
            // Check the connection
            if (system.IsConnected(cwbcoServiceEnum.cwbcoServiceRemoteCmd) == 1)
            {
                // Create a program object and link to a system                
                cwbx.Program program = new cwbx.Program();
                program.LibraryName = "LIBRARY";
                program.ProgramName = "RPGPROG";
                program.system = system;
 
                // Sample parameter data
                string param = "Example";
                int paramLength = 15; // Will be defined in the RGP program, so check with the programmer.
 
                // Create a collection of parameters associated with the program
                ProgramParameters parameters = new ProgramParameters();
                parameters.Append("in", cwbrcParameterTypeEnum.cwbrcInout, paramLength);
                parameters.Append("out", cwbrcParameterTypeEnum.cwbrcInout, paramLength);
                parameters["in"].Value = stringConverter.ToBytes(param.PadRight(paramLength, ' '));
 
                // Finally call the program 
                try
                {
                    program.Call(parameters);
                }
                catch (Exception ex)
                {
                    if (system.Errors.Count > 0)
                    {
                        foreach (cwbx.Error error in as400.Errors)
                        {
                            Console.WriteLine(error.Text);
                        }
                    }
 
                    if (program.Errors.Count > 0)
                    {
                        foreach (cwbx.Error error in program.Errors)
                        {
                            Console.WriteLine(error.Text);
                        }
                    }
                }
 
                result = stringConverter.FromBytes(parameters["out"].Value);
            }
 
            system.Disconnect(cwbcoServiceEnum.cwbcoServiceAll);
            Console.WriteLine(result);
            Console.ReadKey();
        }
    }
}

While this program does work, I found the performance very slow if you are using multiple calls. It takes an age to create a new AS400System object and connect, and it takes a while for the stringConverter to convert a string to a EBCDIC byte array.


If a program falls over on the AS400 for whatever reason, the exception message contained within Ex.Message will contain the AS400 error code and message, so you can also use that to trap errors. For example, ex.Message may contain "CPA3138 – Member BLAH file BLAHBLAH at maximum size." which you can handle.


Here is my solution to the performance issues surrounding the stringConverter class. You can use this in place of stringConverter to save seconds off parameter string conversion times.


/// <summary>
/// Convert an IBM EBCDIC string into ASCII
/// </summary>
/// <param name="strEBCDICString">IBM AS400 EBCDIC string</param>
/// <returns>ASCII String</returns>
public static string ConvertEBCDICtoASCII(byte[] strEBCDICString)
{
    StringBuilder sb = new StringBuilder();
    char newc = '\0';
 
    strEBCDICString = TrimByteArray(strEBCDICString);
 
    for (int i = 0; i < strEBCDICString.Length; i++)
    {
        if (strEBCDICString[i] != '\0')
        {
            newc = Convert.ToChar(e2a[(int)strEBCDICString[i]]);
            sb.Append(newc);
        }
    }
    string result = sb.ToString();
    sb = null;
 
    return result;
}
 
/// <summary>
/// Convert an ASCII string to IBM EBCDIC
/// </summary>
/// <param name="strASCIIString">The ASCII string to convert</param>
/// <returns>IBM EBCDIC array</returns>
public static byte[] ConvertASCIItoEBCDIC(string strASCIIString)
{
    UTF8Encoding encoding = new UTF8Encoding();
    byte[] result = encoding.GetBytes(strASCIIString);
 
    for (int i = 0; i < result.Length; i++)
    {
        result[i] = a2e[(int)result[i]];
    }
 
    return result;
}
 
/// <summary>
/// Character lookup for EBCDIC ASCII Conversion
/// </summary>
private static int[] e2a = new int[256]{
0, 1, 2, 3,156, 9,134,127,151,141,142, 11, 12, 13, 14, 15,
16, 17, 18, 19,157,133, 8,135, 24, 25,146,143, 28, 29, 30, 31,
128,129,130,131,132, 10, 23, 27,136,137,138,139,140, 5, 6, 7,
144,145, 22,147,148,149,150, 4,152,153,154,155, 20, 21,158, 26,
32,160,161,162,163,164,165,166,167,168, 91, 46, 60, 40, 43, 33,
38,169,170,171,172,173,174,175,176,177, 93, 36, 42, 41, 59, 94,
45, 47,178,179,180,181,182,183,184,185,124, 44, 37, 95, 62, 63,
186,187,188,189,190,191,192,193,194, 96, 58, 35, 64, 39, 61, 34,
195, 97, 98, 99,100,101,102,103,104,105,196,197,198,199,200,201,
202,106,107,108,109,110,111,112,113,114,203,204,205,206,207,208,
209,126,115,116,117,118,119,120,121,122,210,211,212,213,214,215,
216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,
123, 65, 66, 67, 68, 69, 70, 71, 72, 73,232,233,234,235,236,237,
125, 74, 75, 76, 77, 78, 79, 80, 81, 82,238,239,240,241,242,243,
92,159, 83, 84, 85, 86, 87, 88, 89, 90,244,245,246,247,248,249,
48, 49, 50, 51, 52, 53, 54, 55, 56, 57,250,251,252,253,254,255
};
 
/// <summary>
/// Character lookup for EBCDIC ASCII Conversion
/// </summary>
private static byte[] a2e = new byte[256]{
0, 1, 2, 3, 55, 45, 46, 47, 22, 5, 37, 11, 12, 13, 14, 15,
16, 17, 18, 19, 60, 61, 50, 38, 24, 25, 63, 39, 28, 29, 30, 31,
64, 79,127,123, 91,108, 80,125, 77, 93, 92, 78,107, 96, 75, 97,
240,241,242,243,244,245,246,247,248,249,122, 94, 76,126,110,111,
124,193,194,195,196,197,198,199,200,201,209,210,211,212,213,214,
215,216,217,226,227,228,229,230,231,232,233, 74,224, 90, 95,109,
121,129,130,131,132,133,134,135,136,137,145,146,147,148,149,150,
151,152,153,162,163,164,165,166,167,168,169,192,106,208,161, 7,
32, 33, 34, 35, 36, 21, 6, 23, 40, 41, 42, 43, 44, 9, 10, 27,
48, 49, 26, 51, 52, 53, 54, 8, 56, 57, 58, 59, 4, 20, 62,225,
65, 66, 67, 68, 69, 70, 71, 72, 73, 81, 82, 83, 84, 85, 86, 87,
88, 89, 98, 99,100,101,102,103,104,105,112,113,114,115,116,117,
118,119,120,128,138,139,140,141,142,143,144,154,155,156,157,158,
159,160,170,171,172,173,174,175,176,177,178,179,180,181,182,183,
184,185,186,187,188,189,190,191,202,203,204,205,206,207,218,219,
220,221,222,223,234,235,236,237,238,239,250,251,252,253,254,255
};

All this does is simply convert one format to another using a lookup table. Much, much faster than whatever the IBM library is doing. To use it simply replace:

parameters["in"].Value = stringConverter.ToBytes(param.PadRight(paramLength, ' '));

with

parameters["in"].Value = ConvertASCIItoEBCDIC(param.PadRight(paramLength, ' '))

and

result = stringConverter.FromBytes(parameters["out"].Value);

with

result = ConvertEBCDICtoASCII(parameters["out"].Value);

Other performance issues surround the use of the AS400System object. It seems to take around 3-5 seconds to create a new object and connect to the system. There are a few ways around this, but the one I prefer is to use an object factory class or singleton to dispense connections. If you are working on Windows Forms then you can get away with creating a "global" variable to hold the AS400System, while on ASP.Net you can use a pool of objects and a singleton dispenser.

 

<Previous   Next>