在 C# 中编程 IBM iSeries API QUSLSPL
Programming IBM iSeries API QUSLSPL in C#
谁能帮我用完整的 c# 代码调用 SPLF0200 格式的 QUSLSPL。我可以调用该程序,但不知道如何 capture/read 通过输出。我是这个领域的新手。感谢您的帮助。
这是我的代码。
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);
接下来呢?在哪里以及如何读取输出?感谢您的回复。
您选择了 API 的嗡嗡声作为开始。您有一个用户 space 列表 API,列表格式为 all-key。那些很复杂。我会尽力解释这一切。系好安全带!
QUSLSPL API 除了错误结构中的内容外,return 什么都不做。其他一切都是输入参数。要访问由 API 生成的假脱机文件列表,您必须访问用户 space object。在您的示例中,用户 space 是 QGPL/HRAHMAN。在我开始检查用户 space 的输出之前,让我们了解一下如何使用用户 space.
什么是用户 space?
用户space只是存储在主机系统库中的一个大旧字节块,最大大小为 16,776,704 字节。您不仅可以将它们用于列出 API 结果,但这就是我真正使用它们的全部目的。需要用户 space 的列表 API 的步骤如下:
- 创建用户space.
- 调用 API.
- 检查来自 API 的错误。
- 找出每个条目的大小。
- 找到列表数据的开始。
- 遍历用户 space 中的条目。
- 删除用户space。
创建用户space
创建用户 space 是通过创建用户 Space (QUSCRTUS) API 完成的。这个API漂亮straight-forward。您将用户的限定名称 space、一些初始值和 API 错误结构传递给它(这样您就可以处理出现的问题)。 API 定义可以在这里找到:http://www.ibm.com/support/knowledgecenter/ssw_ibm_i_71/apis/quscrtus.htm
参数为:
- 完全限定名称 (char[20])
- 扩展属性 (char[10])
- 初始大小(二进制[4])
- 初始值(字符[1])
- Public权限(char[10])
- 文本说明 (char[50])
- 替换 (char[10])
- API 错误结构
从用户检索数据space
调用 QUSLSPL API 后,您需要从用户 space 检索数据。为此,您使用 QUSRTVUS API。此 API 采用用户 space 名称、起始位置、长度、接收者变量和 API 错误结构。 API 定义在这里:http://www.ibm.com/support/knowledgecenter/ssw_ibm_i_71/apis/qusrtvus.htm
参数为:
- 完全限定名称 (char[20])
- 起始位置(二进制[4])注意:这是从1开始的,而不是zero-based。
- 数据长度为return(二进制[4])
- 接收者变量 (*)
- API 错误结构
删除用户space
完成后,使用 QUSDLTUS API 删除用户 space。这个更简单,它采用完全限定名称和 API 错误结构。 API 定义可在此处找到:http://www.ibm.com/support/knowledgecenter/ssw_ibm_i_71/apis/qusdltus.htm
列表API用户中的结构Space
以特定格式向用户 space 列出 APIs return 数据。它看起来像这样:
- 一个用户区
- 通用的header
- 一个输入参数部分
- 一个header节
- 列表数据部分
就通读列表 API 而言,真正重要的是在通用 header 中用户前面的以下值 space。请注意,这些位置是 zero-based.
- 位置 0x7c:列表数据部分的偏移量
- 位置 0x84:列表条目数
- 位置 0x88:每个条目的大小
根据这些信息,您可以分块读取用户 space。每个块从偏移量+(当前zero-based条目号*每个条目的大小)开始,并运行条目大小的长度。
查看 QUSLSPL 的结果
列表中的每个条目 return 从 QUSLSPL 编辑的格式 SPLF0200 有两部分。前 4 个字节包含字段计数 returned。然后它具有为每个字段重复的字段数据结构。字段数据结构的大小是可变的。您必须为每个字段遍历它,查看字段键,并使用它来确定哪个值得到 returned。最终结果是 two-level 循环。外循环循环遍历每个假脱机文件条目。内循环遍历以 SPLF0200 格式 return 编辑的每个字段。
这是一些示例代码,基于您的原始问题。一些注意事项,首先:
- 我没有在 API 调用周围加入错误检查或 try/catch 逻辑,但生产程序会有这些。
- 我可能会将用户 space API 调用放入他们自己的 class 以实现可重用性。
- 我更改了您设置输入参数值的方式,使其更加精简。
- 我使用一个 StringConverter 和 LongConverter 进行所有转换。
另请注意,我稍微更改了参数以调出当前用户的假脱机文件,因为在测试中阅读此示例时,我不想在当前作业中生成假脱机数据。
//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;
}
谁能帮我用完整的 c# 代码调用 SPLF0200 格式的 QUSLSPL。我可以调用该程序,但不知道如何 capture/read 通过输出。我是这个领域的新手。感谢您的帮助。
这是我的代码。
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);
接下来呢?在哪里以及如何读取输出?感谢您的回复。
您选择了 API 的嗡嗡声作为开始。您有一个用户 space 列表 API,列表格式为 all-key。那些很复杂。我会尽力解释这一切。系好安全带!
QUSLSPL API 除了错误结构中的内容外,return 什么都不做。其他一切都是输入参数。要访问由 API 生成的假脱机文件列表,您必须访问用户 space object。在您的示例中,用户 space 是 QGPL/HRAHMAN。在我开始检查用户 space 的输出之前,让我们了解一下如何使用用户 space.
什么是用户 space?
用户space只是存储在主机系统库中的一个大旧字节块,最大大小为 16,776,704 字节。您不仅可以将它们用于列出 API 结果,但这就是我真正使用它们的全部目的。需要用户 space 的列表 API 的步骤如下:
- 创建用户space.
- 调用 API.
- 检查来自 API 的错误。
- 找出每个条目的大小。
- 找到列表数据的开始。
- 遍历用户 space 中的条目。
- 删除用户space。
创建用户space
创建用户 space 是通过创建用户 Space (QUSCRTUS) API 完成的。这个API漂亮straight-forward。您将用户的限定名称 space、一些初始值和 API 错误结构传递给它(这样您就可以处理出现的问题)。 API 定义可以在这里找到:http://www.ibm.com/support/knowledgecenter/ssw_ibm_i_71/apis/quscrtus.htm
参数为:
- 完全限定名称 (char[20])
- 扩展属性 (char[10])
- 初始大小(二进制[4])
- 初始值(字符[1])
- Public权限(char[10])
- 文本说明 (char[50])
- 替换 (char[10])
- API 错误结构
从用户检索数据space
调用 QUSLSPL API 后,您需要从用户 space 检索数据。为此,您使用 QUSRTVUS API。此 API 采用用户 space 名称、起始位置、长度、接收者变量和 API 错误结构。 API 定义在这里:http://www.ibm.com/support/knowledgecenter/ssw_ibm_i_71/apis/qusrtvus.htm
参数为:
- 完全限定名称 (char[20])
- 起始位置(二进制[4])注意:这是从1开始的,而不是zero-based。
- 数据长度为return(二进制[4])
- 接收者变量 (*)
- API 错误结构
删除用户space
完成后,使用 QUSDLTUS API 删除用户 space。这个更简单,它采用完全限定名称和 API 错误结构。 API 定义可在此处找到:http://www.ibm.com/support/knowledgecenter/ssw_ibm_i_71/apis/qusdltus.htm
列表API用户中的结构Space
以特定格式向用户 space 列出 APIs return 数据。它看起来像这样:
- 一个用户区
- 通用的header
- 一个输入参数部分
- 一个header节
- 列表数据部分
就通读列表 API 而言,真正重要的是在通用 header 中用户前面的以下值 space。请注意,这些位置是 zero-based.
- 位置 0x7c:列表数据部分的偏移量
- 位置 0x84:列表条目数
- 位置 0x88:每个条目的大小
根据这些信息,您可以分块读取用户 space。每个块从偏移量+(当前zero-based条目号*每个条目的大小)开始,并运行条目大小的长度。
查看 QUSLSPL 的结果
列表中的每个条目 return 从 QUSLSPL 编辑的格式 SPLF0200 有两部分。前 4 个字节包含字段计数 returned。然后它具有为每个字段重复的字段数据结构。字段数据结构的大小是可变的。您必须为每个字段遍历它,查看字段键,并使用它来确定哪个值得到 returned。最终结果是 two-level 循环。外循环循环遍历每个假脱机文件条目。内循环遍历以 SPLF0200 格式 return 编辑的每个字段。
这是一些示例代码,基于您的原始问题。一些注意事项,首先:
- 我没有在 API 调用周围加入错误检查或 try/catch 逻辑,但生产程序会有这些。
- 我可能会将用户 space API 调用放入他们自己的 class 以实现可重用性。
- 我更改了您设置输入参数值的方式,使其更加精简。
- 我使用一个 StringConverter 和 LongConverter 进行所有转换。
另请注意,我稍微更改了参数以调出当前用户的假脱机文件,因为在测试中阅读此示例时,我不想在当前作业中生成假脱机数据。
//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;
}