Progtwigción de IBM iSeries API QUSLSPL en C #

¿Puede alguien ayudarme con el código c # completo para llamar a QUSLSPL con formato SPLF0200? Puedo llamar al progtwig pero no sé cómo capturar / leer a través de la salida. Soy nuevo en esta área. Aprecio tu ayuda.

Aquí está mi código.

cwbx.ProgramParameters parameters = new cwbx.ProgramParameters(); //user space name parameters.Append("usrspcnam", cwbrcParameterTypeEnum.cwbrcInout, 20); StringConverter stringConverterUsrSpaceNm = new cwbx.StringConverter(); stringConverterUsrSpaceNm.Length = 20; parameters["usrspcnam"].Value = stringConverterUsrSpaceNm.ToBytes("HRAHMAN QGPL "); //Format parameters.Append("frmname", cwbrcParameterTypeEnum.cwbrcInout, 8); StringConverter stringConverterFrmname = new cwbx.StringConverter(); stringConverterFrmname.Length = 8; parameters["frmname"].Value = stringConverterFrmname.ToBytes("SPLF0200"); //User Name parameters.Append("usrnam", cwbrcParameterTypeEnum.cwbrcInout, 10); StringConverter stringConverterUsrnam = new cwbx.StringConverter(); stringConverterUsrnam.Length = 10; //parameters["usrnam"].Value = stringConverterUsrnam.ToBytes("*CURRENT"); parameters["usrnam"].Value = stringConverterUsrnam.ToBytes(" "); //qualified output queue parameters.Append("cola", cwbrcParameterTypeEnum.cwbrcInout, 20); StringConverter stringConverterCola = new cwbx.StringConverter(); stringConverterCola.Length = 20; //parameters["cola"].Value = stringConverterCola.ToBytes("*ALL"); parameters["cola"].Value = stringConverterCola.ToBytes(" "); //form type parameters.Append("frmtyp", cwbrcParameterTypeEnum.cwbrcInout, 10); StringConverter stringConverterFrmtyp = new cwbx.StringConverter(); stringConverterFrmtyp.Length = 10; //parameters["frmtyp"].Value = stringConverterFrmtyp.ToBytes("*ALL"); parameters["frmtyp"].Value = stringConverterFrmtyp.ToBytes(" "); //user-specific data parameters.Append("usrdta", cwbrcParameterTypeEnum.cwbrcInout, 10); StringConverter stringConverterUsrdta = new cwbx.StringConverter(); stringConverterUsrdta.Length = 10; //parameters["usrdta"].Value = stringConverterUsrdta.ToBytes("*ALL"); parameters["usrdta"].Value = stringConverterUsrdta.ToBytes(" "); //error parameters.Append("error", cwbrcParameterTypeEnum.cwbrcInout, 116); Structure sc2 = new Structure(); sc2.Fields.Append("bytesprov", 4); sc2.Fields.Append("bytesavail", 4); sc2.Fields.Append("messageid", 7); sc2.Fields.Append("err", 1); sc2.Fields.Append("messagedta", 100); parameters["error"].Value = sc2.Bytes; //qualified job name parameters.Append("qualifiedjobnm", cwbrcParameterTypeEnum.cwbrcInput, 26); StringConverter stringConverterUsrdta1 = new cwbx.StringConverter(); stringConverterUsrdta1.Length = 26; parameters["qualifiedjobnm"].Value = stringConverterUsrdta1.ToBytes("* "); //keys parameters.Append("keys", cwbrcParameterTypeEnum.cwbrcInput, 44); //44 is 11 keys times 4 bytes per key LongConverter lc = new cwbx.LongConverter(); Structure keys = new Structure(); keys.Fields.Append("Spooledfilename", 4); //char10 201 keys.Fields["Spooledfilename"].Value = lc.ToBytes(201); keys.Fields.Append("Username", 4); //char10 203 keys.Fields["Username"].Value = lc.ToBytes(203); keys.Fields.Append("opqueue", 4); //206 keys.Fields["opqueue"].Value = lc.ToBytes(206); keys.Fields.Append("userdata", 4); //209 keys.Fields["userdata"].Value = lc.ToBytes(209); keys.Fields.Append("status", 4); //210 keys.Fields["status"].Value = lc.ToBytes(210); keys.Fields.Append("totpages", 4); //bin 211 keys.Fields["totpages"].Value = lc.ToBytes(211); keys.Fields.Append("copies", 4); //bin 213 keys.Fields["copies"].Value = lc.ToBytes(213); keys.Fields.Append("openeddate", 4); //216 keys.Fields["openeddate"].Value = lc.ToBytes(216); keys.Fields.Append("opentime", 4); //217 keys.Fields["opentime"].Value = lc.ToBytes(217); keys.Fields.Append("jobid", 4); //218 keys.Fields["jobid"].Value = lc.ToBytes(218); keys.Fields.Append("fileid", 4); //219 keys.Fields["fileid"].Value = lc.ToBytes(219); parameters["keys"].Value = keys.Bytes; //number of keys to return parameters.Append("numberoffields", cwbrcParameterTypeEnum.cwbrcInput, 4); LongConverter LongConverterKeys = new cwbx.LongConverter(); parameters["numberoffields"].Value = LongConverterKeys.ToBytes(11); //11 keys in total program.Invoke(true, ref parameters); 

Ahora que sigue ¿Dónde y cómo leer la salida? Aprecie su respuesta.

Escogiste un humdinger de una API para empezar. Tiene una API de lista de espacio de usuario con un formato de lista de todas las claves. Esos son complejos. Haré mi mejor esfuerzo para explicarlo todo. ¡Cinturón de seguridad!

La API QUSLSPL no devuelve nada excepto lo que está en la estructura de error. Todo lo demás es un parámetro de entrada. Para acceder a la lista de archivos en spool generada por la API, debe acceder al objeto de espacio de usuario. En su ejemplo, el espacio de usuario es QGPL / HRAHMAN. Antes de comenzar a examinar la salida del espacio de usuario, vamos a entender cómo usar un espacio de usuario.

¿Qué es un espacio de usuario?

Un espacio de usuario es solo un gran bloque de bytes almacenado en una biblioteca en el sistema host con un tamaño máximo de 16,776,704 bytes. Puede usarlos para más que simplemente listar los resultados de la API, pero eso es todo para lo que realmente los uso. Los pasos para las API de lista que requieren espacios de usuario son los siguientes:

  1. Crear espacio de usuario.
  2. Llame a la API.
  3. Compruebe si hay errores de la API.
  4. Encuentra el tamaño de cada entrada.
  5. Encuentra el inicio de la lista de datos.
  6. Recorre las entradas en el espacio de usuario.
  7. Eliminar el espacio de usuario.

Crea el espacio de usuario.

La creación del espacio de usuario se realiza a través de la API Crear espacio de usuario (QUSCRTUS). Esta API es bastante sencilla. Le pasa un nombre calificado del espacio de usuario, algunos valores iniciales y la estructura de error de la API (para que pueda manejar los problemas que surjan). La definición de la API se puede encontrar aquí: http://www.ibm.com/support/knowledgecenter/ssw_ibm_i_71/apis/quscrtus.htm

Los parámetros son:

  • Nombre completo (char [20])
  • Atributo extendido (char [10])
  • Tamaño inicial (binario [4])
  • Valor inicial (char [1])
  • Autoridad pública (char [10])
  • Descripción del texto (char [50])
  • Reemplazar (char [10])
  • Estructura de error API

Recuperar datos del espacio de usuario

Después de llamar a la API de QUSLSPL, debe recuperar los datos del espacio de usuario. Para eso usas la API QUSRTVUS. Esta API toma el nombre del espacio de usuario, la posición inicial, la longitud, la variable del receptor y la estructura de error de la API. La definición de la API está aquí: http://www.ibm.com/support/knowledgecenter/ssw_ibm_i_71/apis/qusrtvus.htm

Los parámetros son:

  • Nombre completo (char [20])
  • Posición inicial (binario [4]) Nota: esta es basada en 1, no en base a cero.
  • Longitud de los datos a devolver (binario [4])
  • Variable del receptor (*)
  • Estructura de error API

Eliminar el espacio de usuario

Cuando haya terminado, elimine el espacio de usuario utilizando la API QUSDLTUS. Este es aún más fácil, toma el nombre completo y la estructura de error de la API. La definición de la API se encuentra aquí: http://www.ibm.com/support/knowledgecenter/ssw_ibm_i_71/apis/qusdltus.htm

Lista de estructura de API en el espacio de usuario

Las API de lista devuelven datos al espacio de usuario en un formato específico. Se parece a esto:

  • Un area de usuario
  • Un encabezado genérico
  • Una sección de parámetros de entrada
  • Una sección de cabecera
  • Una sección de datos de la lista.

Lo que realmente importa, en lo que respecta a la lectura de una lista de API son los siguientes valores en el frente del espacio del usuario, en ese encabezado genérico. Tenga en cuenta que estas posiciones están basadas en cero.

  • Posición 0x7c: Desplazamiento a la sección de datos de la lista
  • Posición 0x84: Número de entradas de la lista
  • Posición 0x88: Tamaño de cada entrada.

Con esta información, lees el espacio de usuario en trozos. Cada fragmento comienza en offset + (número de entrada actual basado en cero * tamaño de cada entrada) y se ejecuta para la longitud del tamaño de la entrada.

Mirando los resultados de QUSLSPL

Cada entrada en la lista devuelta de QUSLSPL para el formato SPLF0200 tiene dos partes. Los primeros 4 bytes contienen un recuento de campos devueltos. Luego tiene la estructura de datos de campo repetida para cada campo. El tamaño de la estructura de datos de campo es variable. Tiene que recorrerlo para cada campo, mirar la clave de campo y usar ese valor para determinar qué valor se devolvió. El resultado final es un bucle de dos niveles. El ciclo externo recorre cada entrada de archivo en spool. El ciclo interno recorre cada campo devuelto en el formato SPLF0200.

Aquí hay un código de ejemplo, basado en su pregunta original. Algunas notas, primero:

  • No puse la comprobación de errores ni la lógica de prueba / captura en torno a las llamadas a la API, pero un progtwig de producción tendría eso.
  • Probablemente pondría las llamadas a la API del espacio de usuario en su propia clase para su reutilización.
  • Cambié la forma en que configuras los valores de tus parámetros de entrada para que sean más ágiles.
  • Uso un solo StringConverter y LongConverter para todas las conversiones.

También tenga en cuenta que modifiqué ligeramente los parámetros para mostrar los archivos en cola del usuario actual porque, al probar esta muestra, no quería tener que generar datos en cola en el trabajo actual.

 //Define a single StringConverter and LongConverter to re-use cwbx.StringConverter stringConverter = new cwbx.StringConverter(); cwbx.LongConverter longConverter = new cwbx.LongConverter(); //Type the user space name only once. It's re-used a lot. String userSpaceName = "HRAHMAN QGPL "; //Connect to the AS/400 AS400System as400 = new AS400System(); as400.Define("MY_SYSTEM_HOST_ADDRESS"); as400.UserID = "MY_USER"; as400.Password = "MY_PASSWORD"; as400.Connect(cwbcoServiceEnum.cwbcoServiceRemoteCmd); //Define the error structure once, to be re-used a lot. Structure sc2 = new Structure(); sc2.Fields.Append("bytesprov", 4); sc2.Fields.Append("bytesavail", 4); sc2.Fields.Append("messageid", 7); sc2.Fields.Append("err", 1); sc2.Fields.Append("messagedta", 100); sc2.Fields["bytesavail"].Value = longConverter.ToBytes(sc2.Length); //Create the user space cwbx.Program quscrtus = new cwbx.Program(); quscrtus.system = as400; quscrtus.LibraryName = "QSYS"; quscrtus.ProgramName = "QUSCRTUS"; cwbx.ProgramParameters quscrtusParms = new cwbx.ProgramParameters(); quscrtusParms.Append("UserSpaceName", cwbrcParameterTypeEnum.cwbrcInput, 20).Value = stringConverter.ToBytes(userSpaceName); quscrtusParms.Append("ExtendedAttr", cwbrcParameterTypeEnum.cwbrcInput, 10).Value = stringConverter.ToBytes("".PadRight(10)); quscrtusParms.Append("InitialSize", cwbrcParameterTypeEnum.cwbrcInput, 4).Value = longConverter.ToBytes(1); quscrtusParms.Append("InitialValue", cwbrcParameterTypeEnum.cwbrcInput, 1).Value = longConverter.ToBytes(0); quscrtusParms.Append("Auth", cwbrcParameterTypeEnum.cwbrcInput, 10).Value = stringConverter.ToBytes("*ALL".PadRight(10)); quscrtusParms.Append("Desc", cwbrcParameterTypeEnum.cwbrcInput, 50).Value = stringConverter.ToBytes("QUSLSPL Results".PadRight(50)); quscrtusParms.Append("Replace", cwbrcParameterTypeEnum.cwbrcInput, 10).Value = stringConverter.ToBytes("*YES".PadRight(10)); quscrtusParms.Append("APIError", cwbrcParameterTypeEnum.cwbrcInout, sc2.Length).Value = sc2.Bytes; quscrtus.Call(quscrtusParms); sc2.Bytes = quscrtusParms["APIError"].Value; if (((string)stringConverter.FromBytes(sc2.Fields["messageid"].Value)).Trim().Length > 0) { //deal with error return; } //Call the list spooled files API cwbx.Program quslspl = new cwbx.Program(); quslspl.system = as400; quslspl.LibraryName = "QSYS"; quslspl.ProgramName = "QUSLSPL"; ProgramParameters quslsplParms = new cwbx.ProgramParameters(); quslsplParms.Append("usrspcnam", cwbrcParameterTypeEnum.cwbrcInput, 20).Value = stringConverter.ToBytes(userSpaceName); //user space name quslsplParms.Append("frmname", cwbrcParameterTypeEnum.cwbrcInput, 8).Value = stringConverter.ToBytes("SPLF0200"); //Format quslsplParms.Append("usrnam", cwbrcParameterTypeEnum.cwbrcInput, 10).Value = stringConverter.ToBytes("*CURRENT".PadRight(10)); //User Name quslsplParms.Append("cola", cwbrcParameterTypeEnum.cwbrcInput, 20).Value = stringConverter.ToBytes("*ALL".PadRight(20)); //qualified output queue quslsplParms.Append("frmtyp", cwbrcParameterTypeEnum.cwbrcInput, 10).Value = stringConverter.ToBytes("*ALL".PadRight(10)); //form type quslsplParms.Append("usrdta", cwbrcParameterTypeEnum.cwbrcInput, 10).Value = stringConverter.ToBytes("*ALL".PadRight(10)); //user-specific data quslsplParms.Append("error", cwbrcParameterTypeEnum.cwbrcInout, sc2.Length).Value = sc2.Bytes; //error quslsplParms.Append("qualifiedjobnm", cwbrcParameterTypeEnum.cwbrcInput, 26).Value = stringConverter.ToBytes("".PadRight(26)); //qualified job name //keys. The SPLF0200 structure uses a list of field keys. So we tell the API which keys we want and that's what it returns. cwbx.Structure keys = new cwbx.Structure(); keys.Fields.Append("Spooledfilename", 4).Value = longConverter.ToBytes(201); keys.Fields.Append("Username", 4).Value = longConverter.ToBytes(203); keys.Fields.Append("opqueue", 4).Value = longConverter.ToBytes(206); keys.Fields.Append("userdata", 4).Value = longConverter.ToBytes(209); keys.Fields.Append("status", 4).Value = longConverter.ToBytes(210); keys.Fields.Append("totpages", 4).Value = longConverter.ToBytes(211); keys.Fields.Append("copies", 4).Value = longConverter.ToBytes(213); keys.Fields.Append("openeddate", 4).Value = longConverter.ToBytes(216); keys.Fields.Append("opentime", 4).Value = longConverter.ToBytes(217); keys.Fields.Append("jobid", 4).Value = longConverter.ToBytes(218); keys.Fields.Append("fileid", 4).Value = longConverter.ToBytes(219); quslsplParms.Append("keys", cwbrcParameterTypeEnum.cwbrcInput, keys.Length).Value=keys.Bytes; quslsplParms.Append("numberoffields", cwbrcParameterTypeEnum.cwbrcInput, 4).Value = longConverter.ToBytes(keys.Fields.Count); //number of keys to return quslspl.Call(quslsplParms); sc2.Bytes = quslsplParms["error"].Value; if (((string)stringConverter.FromBytes(sc2.Fields["messageid"].Value)).Trim().Length > 0) { //deal with error return; } //Get the list information from the user space cwbx.Structure listInfo = new cwbx.Structure(); listInfo.Fields.Append("OffsetToData", 4); listInfo.Fields.Append("DataSectionSize", 4); listInfo.Fields.Append("NumberOfEntries", 4); listInfo.Fields.Append("EntrySize", 4); //The List information data structure starts at zero-based position 0x7c. The retrieve user space //API uses 1-based indexing. Retreive the list information from the user space. cwbx.Program qusrtvus = new cwbx.Program(); qusrtvus.system = as400; qusrtvus.LibraryName = "QSYS"; qusrtvus.ProgramName = "QUSRTVUS"; cwbx.ProgramParameters qusrtvusParms = new cwbx.ProgramParameters(); qusrtvusParms.Append("UserSpaceName", cwbrcParameterTypeEnum.cwbrcInput, 20).Value = stringConverter.ToBytes(userSpaceName); qusrtvusParms.Append("StartingPosition", cwbrcParameterTypeEnum.cwbrcInput, 4).Value = longConverter.ToBytes(0x7c + 1); qusrtvusParms.Append("Length", cwbrcParameterTypeEnum.cwbrcInput, 4).Value = longConverter.ToBytes(listInfo.Length); qusrtvusParms.Append("Receiver", cwbrcParameterTypeEnum.cwbrcInout, listInfo.Length); qusrtvusParms.Append("APIError", cwbrcParameterTypeEnum.cwbrcInout, sc2.Length).Value = sc2.Bytes; qusrtvus.Call(qusrtvusParms); sc2.Bytes = qusrtvusParms["APIError"].Value; if (((string)stringConverter.FromBytes(sc2.Fields["messageid"].Value)).Trim().Length > 0) { //deal with error return; } listInfo.Bytes = qusrtvusParms["Receiver"].Value; int offsetToData = longConverter.FromBytes(listInfo.Fields["OffsetToData"].Value); int numberOfEntries = longConverter.FromBytes(listInfo.Fields["NumberOfEntries"].Value); int entrySize = longConverter.FromBytes(listInfo.Fields["EntrySize"].Value); //Define the structure to receive the SPLF0200 Field data. This is described in the QUSLSPL API. //Note: According to the API documentation, this is the only part that repeats for each key. The first //four bytes of the SPLF0200 structure is the count of keys returned. cwbx.Structure SPLF0200Field = new cwbx.Structure(); //individual field value data SPLF0200Field.Fields.Append("LengthOfInformation", 4); SPLF0200Field.Fields.Append("KeyField", 4); SPLF0200Field.Fields.Append("TypeOfData", 1); SPLF0200Field.Fields.Append("Reserved", 3); SPLF0200Field.Fields.Append("LengthOfData", 4); //Loop through each entry in the list and get the field values by key for (int currentEntry = 0; currentEntry < numberOfEntries; currentEntry++) { qusrtvusParms["StartingPosition"].Value = longConverter.ToBytes(offsetToData + (currentEntry * entrySize) + 1); qusrtvusParms["Length"].Value = longConverter.ToBytes(entrySize); qusrtvusParms["Receiver"].Length = entrySize; qusrtvus.Call(qusrtvusParms); sc2.Bytes = qusrtvusParms["APIError"].Value; if (((string)stringConverter.FromBytes(sc2.Fields["messageid"].Value)).Trim().Length > 0) { //deal with error return; } //According to the SPLF0200 format, the first 4-byte integer is the number of fields returned. //After that, it's a variable list of key structures. byte[] entry = qusrtvusParms["Receiver"].Value; byte[] numberOfFieldsReturnedBytes = new byte[4]; Array.Copy(entry, 0, numberOfFieldsReturnedBytes, 0, 4); int numberOfFieldsReturned = longConverter.FromBytes(numberOfFieldsReturnedBytes); int lastBufferEnd = 4; //Fields to hold the spooled file field elements. Note: In a production environment, I would normally //create a class to hold all of this, but this is just for sample purposes. String spooledFileName = ""; String userName = ""; String opqueue = ""; String userdata = ""; String status = ""; int totpages = 0; int copies = 0; String openeddate = ""; String opentime = ""; byte[] jobid = new byte[16]; byte[] fileid = new byte[16]; for (int currentField = 0; currentField < numberOfFieldsReturned; currentField++) { byte[] SPLF0200FieldBytes = new byte[SPLF0200Field.Length]; Array.Copy(entry, lastBufferEnd, SPLF0200FieldBytes, 0, SPLF0200FieldBytes.Length); SPLF0200Field.Bytes = SPLF0200FieldBytes; int fieldDataLength = longConverter.FromBytes(SPLF0200Field.Fields["LengthOfData"].Value); int fieldInfoLength = longConverter.FromBytes(SPLF0200Field.Fields["LengthOfInformation"].Value); int fieldKey = longConverter.FromBytes(SPLF0200Field.Fields["KeyField"].Value); byte[] fieldDataBytes = new byte[fieldDataLength]; Array.Copy(entry, lastBufferEnd + 16, fieldDataBytes, 0, fieldDataLength); lastBufferEnd = lastBufferEnd + fieldInfoLength; switch (fieldKey) { case 201: spooledFileName = stringConverter.FromBytes(fieldDataBytes); break; case 203: userName = stringConverter.FromBytes(fieldDataBytes); break; case 206: opqueue = stringConverter.FromBytes(fieldDataBytes); break; case 209: userdata = stringConverter.FromBytes(fieldDataBytes); break; case 210: status = stringConverter.FromBytes(fieldDataBytes); break; case 211: totpages = longConverter.FromBytes(fieldDataBytes); break; case 213: copies = longConverter.FromBytes(fieldDataBytes); break; case 216: openeddate = stringConverter.FromBytes(fieldDataBytes); break; case 217: opentime = stringConverter.FromBytes(fieldDataBytes); break; case 218: jobid = fieldDataBytes; break; case 219: fileid = fieldDataBytes; break; } } //All field elements that the API returned (that we care about) are loaded. //Now do something with the spooled file fields here. } //Delete the user space cwbx.Program qusdltus = new cwbx.Program(); qusdltus.system = as400; qusdltus.LibraryName = "QSYS"; qusdltus.ProgramName = "QUSDLTUS"; cwbx.ProgramParameters qusdltusParms = new cwbx.ProgramParameters(); qusdltusParms.Append("UserSpaceName", cwbrcParameterTypeEnum.cwbrcInput, 20).Value = stringConverter.ToBytes(userSpaceName); qusdltusParms.Append("APIError", cwbrcParameterTypeEnum.cwbrcInout, sc2.Length).Value = sc2.Bytes; qusdltus.Call(qusdltusParms); sc2.Bytes = qusdltusParms["APIError"].Value; if (((string)stringConverter.FromBytes(sc2.Fields["messageid"].Value)).Trim().Length > 0) { //deal with error return; }