Calling an iSeries system API (QWDRSBSD) from C#

Hi Guys,

I'm trying to call the iSeries "Retrieve Subsystem Information API (QWDRSBSD)" from C#, using cwbx.dll and the SBSI0200 format (see documentation at http://www-01.ibm.com/support/knowledgecenter/ssw_ibm_i_72/apis/qwdrsbsd.htm).  The problem is that the 6th field in the format, "Reserved",  is of type Char(*) and doesn't have a specific length.  The field I need to extract from this format is "subsystem extended status" which is the fourth field in the array data following the "Reserved" field.  How do I define this format to the cwbx.dll in such a way as to actually be able to get the "subsystem extended status" data that I need?  Here's how I would ordinarily do the call.  I've commented the line that I don't know how to handle:

//Define an AS400 system and connect to it
            AS400System sys = new AS400System();
            sys.Define(server);
            sys.UserID = userID;
            sys.Password = pwd;
            sys.IPAddress = IPAddress;
            sys.Connect(cwbcoServiceEnum.cwbcoServiceRemoteCmd);

            //Define other needed objects
            cwbx.StringConverter sConverter = new cwbx.StringConverter();
            cwbx.LongConverter lConverter = new cwbx.LongConverter();
            cwbx.Structure recVars = new cwbx.Structure();
            cwbx.Structure errCode = new cwbx.Structure();

            SubsystemModel model = new SubsystemModel();
            List<SubsystemModel> subsystems = new List<SubsystemModel>();
            SubsystemsModel subs = new SubsystemsModel();

            //check the connection
            if (sys.IsConnected(cwbcoServiceEnum.cwbcoServiceRemoteCmd) == 1)
            {
                //Create a program object and link to a system
                cwbx.Program pgm = new cwbx.Program();
                pgm.LibraryName = "QSYS";
                pgm.ProgramName = "QWDRSBSD";
                pgm.system = sys;


                //Build the structure for the receiver variable
                recVars.Fields.Append("BytesReturned", 4);
                recVars.Fields.Append("BytesAvail", 4);
                recVars.Fields.Append("SBSFirstEntryOffset", 4);
                recVars.Fields.Append("NumEntriesReturned", 4);
                recVars.Fields.Append("EntrySize", 4);
                recVars.Fields.Append("Reserved", *);               //this is the field that I don't know what to do with
                recVars.Fields.Append("ArrayData", 18750);

                //Standard Error Reporting Structure
                errCode.Fields.Append("BytesProvided", 4);
                errCode.Fields["BytesProvided"].Value = lConverter.ToBytes(272);
                errCode.Fields.Append("BytesAvail", 4);
                errCode.Fields["BytesAvail"].Value = lConverter.ToBytes(0);
                errCode.Fields.Append("ExceptionID", 7);
                errCode.Fields.Append("Reserved", 1);
                errCode.Fields.Append("ExceptionData", 272);

                string result = string.Empty;
                string SBName = "SubSystemName";
                //Parameter data
                string param2 = "SBSI0200";
                string param3 = SBName.PadRight(10, ' ') + "QGPL      ";
                int param4 = 1;

                        //Create a collection of parameters associated with the program
                        ProgramParameters parameters = new ProgramParameters();
                        parameters.Append("RECVAR", cwbrcParameterTypeEnum.cwbrcOutput, recVars.Length);
                        parameters.Append("RECVARLEN", cwbrcParameterTypeEnum.cwbrcInput, 4);
                        parameters.Append("FMTNAME", cwbrcParameterTypeEnum.cwbrcInput, 8);
                        parameters.Append("SBNAME", cwbrcParameterTypeEnum.cwbrcInput, 20);
                        parameters.Append("ERRCODE", cwbrcParameterTypeEnum.cwbrcInout, errCode.Length);
                        parameters.Append("ARRAYLEN", cwbrcParameterTypeEnum.cwbrcInout, 4);

                        //Set parameter values
                        parameters["RECVARLEN"].Value = lConverter.ToBytes(recVars.Length);
                        parameters["FMTNAME"].Value = sConverter.ToBytes(param2);
                        parameters["SBNAME"].Value = sConverter.ToBytes(param3);
                        parameters["ARRAYLEN"].Value = lConverter.ToBytes(param4);

                        //Call the program

                        try
                        {
                            pgm.Call(parameters);
                        }
                        catch (Exception)
                        {
                           //error routine
                        }
                }
            }

Open in new window


I feel that, somehow, I'm missing something simple here.  Or maybe even a more efficient way of doing this that doesn't involved cwbx, at all.
manchesteraAsked:
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Gary PattersonVP Technology / Senior Consultant Commented:
If it was a "real" parameter, it would be a pointer to a block of storage that would be allocated at runtime by the API, and generally there would be another parameter coming back to give you the length (like "Bytes Returned").  CHAR(*) can be up to 32767 bytes long, but in this case, this isn't a "real" parameter.  It is just a placeholder in case they want to stick some more stuff in here in later versions.

Since there is nothing in there you want at this time, just ignore it.

No way to determine the length at design time - though you can figure it out at runtime using a "trial call" if you really want to.

So what this parm list is telling you is that there is a "header" block of 20 bytes containing Bytes Returned, Bytes Available, Offset to First Entry, Number of Subsystem Entries, and Size of a Subsystem Entry.  Now or in the future there may be a gap of some number of characters, and eventually the subsystem details sections will repeat one after the other starting Offset to First Entry bytes from the beginning of the Receiver structure.

Options:

1: Go big.  Declare the Receiver big - up to 32767, the ArrayData area big.  and then just extract out what you need from it.

2: Safer but more work:  "trial call".  Declare a small receiver variable, based on your best guess of the minimum size needed in most cases.  Call the API, and then check your "Bytes Returned" and "Bytes Available" to see if you got everything you needed. If it isn't, increase the size of your receiver variable, to Bytes Available length and call it again.  This will keep your code from breaking in the future if they use a big chunk of the reserved area.

Anyway, there isn't anything in 'reserved", it isn't a real parameter, and you can drop it.  Just be aware that your ArrayData (more correctly "EverythingElse" :-) ) could conceivably conceivable contain a big variable block of stuff at the front, and you will need to use the Offset To First Entry to figure out where your extended subsystem information starts.  

Also be aware that the Offset To First Entry is from the start of the receiver variable, not from the start of ArrayData.

Hope that helps.
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Gary PattersonVP Technology / Senior Consultant Commented:
One other note:  When I'm dealing with relatively complex APIs like this, I usually prefer to write a RPG or C wrapper program with a simplified parameter structure, create a DB2 external stored procedure out of it, and then use the stored procedure using ADO.NET via the .NET data connector that ships with IBM i Access for Windows.
0
daveslaterCommented:
This question has been classified as abandoned and is closed as part of the Cleanup Program. See the recommendation for more details.
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
IBM System i

From novice to tech pro — start learning today.