QWCRSVAL は RTVSYSVAL コマンド ( システム値の検索 ) に相当する機能を提供する API である。
必須パラメータ・グループ:
1. | レシーバー変数 | 出力 | Char(*) |
2. | レシーバー変数の長さ | 入力 | Binary(4) |
3. | 検索するシステム値の数 | 入力 | Binary(4) |
4. | システム値の名前 | 入力 | Char(10) の配列(*) |
5. | エラー・コード | 入出力 | Char(*) |
レシーバー変数
レシーバー変数 とは API によって値が戻されるが、その戻り値を入れるための変数のことである。
つまり、このレシーバー変数にAPI が値を戻すことになる。
API は、このAPI のように、ひとつの変数に値をすべて戻す場合とユーザー・スペースに値を書き込んで
終了して、後でユーザー・スペースを検索しなければならない場合の 2 通りの戻り値の形式がある。
今回はレシーバー変数は値を戻す形式であるが、どのような形式で戻されるかは
QSYSINC/H のインクルード・ファイルに IBM が提供している場合が多い。
API のコンパイルには 今回の QWCRSVAL であれば
#include <QWCRSVAL.h>
のようにして関数の型の定義をインクルードしなければならない。
この中に QSYSINC/H(QWCRSVAL) 受取りレシーバー変数が
typedef _Packed struct Qwc_Rsval_Sys_Value_Table { char System_Valueロ10ワ; char Type_Data; char Information_Status; int Length_Data; /*char Dataロワ; */ /* Varying length */ } Qwc_Rsval_Sys_Value_Table_t;
や、
typedef _Packed struct Qwc_Rsval_Data_Rtnd { int Number_Sys_Vals_Rtnd; /*int Offset_Sys_Val_Tableロワ;*//* Varying length */ /*Qwc_Rsval_Sys_Value_Table_t System_Valuesロワ;*//* Varying length */ } Qwc_Rsval_Data_Rtnd_t;
のようにして登録されている。通常の簡単な API であればそれらを利用して
レシーバー変数とすればよいのだが、簡単な例は別の機会に譲るとして
今回の API : QWCRSVAL が少し複雑であるのは QWCRSVAL が複数個のシステム値の
取得に対応していることである。
ただし今回は話を簡単にするのと現実、複数個のシステム値を取得したいという事例は
あまり多くはないはずであるからだ。
今回は QWCRSVAL を使って System i のシリアル番号 ( QSRLNBR ) を取得する方法を紹介する。
早速、次のソースを見ていただきたい。
0001.00 #include <stdio.h> 0002.00 #include <stdlib.h> 0003.00 #include <string.h> 0004.00 #include <QWCRSVAL.h> 0005.00 0006.00 #define TRUE 0 0007.00 #define FALSE -1 0008.00 typedef struct { 0009.00 int BYTESPRO; 0010.00 int BYTESAVL; 0011.00 char MSGID[7]; 0012.00 char RESRVD; 0013.00 char EXCPDATA[100]; 0014.00 } ERRSTRUCTURE; /* Define the error return structure */ 0015.00 ERRSTRUCTURE errcode;/* Error Code Structure for RCVMSG */ 0016.00 0017.00 void main(void){ 0018.00 typedef struct { 0019.00 int syssu; 0020.00 int offset; 0021.00 char value[128]; 0022.00 } SYSVAL; 0023.00 SYSVAL sysval; 0024.00 typedef struct { 0025.00 Qwc_Rsval_Sys_Value_Table_t table; 0026.00 char data[256]; 0027.00 } TABLE; 0028.00 TABLE* table; 0029.00 int len, offset; 0030.00 char QSRLNBR[9], value[129]; 0031.00 0032.00 printf("** TESTSYS2: システム値の検索 **\n"); 0033.00 getchar(); 0034.00 errcode.BYTESPRO = 160; 0035.00 errcode.BYTESAVL = 0; 0036.00 /*( システム値 :QSRLNBR )*/ 0037.00 QWCRSVAL(&sysval, sizeof(SYSVAL), 1, "QSRLNBR ", &errcode); 0038.00 if(errcode.BYTESAVL != 0){/* APIERR */ 0039.00 printf("* APiErr\n"); 0040.00 getchar(); 0041.00 exit(-1); 0042.00 }/* APIERR */ 0043.00 offset = sysval.offset - 8; 0044.00 table = (TABLE*)&(sysval.value[offset]); 0045.00 memcpy(QSRLNBR, table->data, 8); 0046.00 QSRLNBR[8] = 0x00; 0047.00 printf("QSRLNBR = %s\n", QSRLNBR); 0048.00 getchar(); 0049.00 }
【解説】
API のレシーバー変数として 構造体 sysval を
0017.00 void main(void){ 0018.00 typedef struct { 0019.00 int syssu; 0020.00 int offset; 0021.00 char value[128]; 0022.00 } SYSVAL; 0023.00 SYSVAL sysval;
として定義しているが
char value[128];
の部分は指定するシステム値の個数によって可変長に変わる部分であるが
0037.00 QWCRSVAL(&sysval, sizeof(SYSVAL), 1, "QSRLNBR ", &errcode);
のように、今回は意図的に 1 個のシステム値しか指定していない。
従って value は 128 バイトもあれば十分であろうという予測の元に定義している。
レシーバー変数の長さは
sizeof(SYSVAL)
として定義しているが、これは API を使うときにやる一般的な定義の方法である。
API のエラー・コードは
0008.00 typedef struct { 0009.00 int BYTESPRO; 0010.00 int BYTESAVL; 0011.00 char MSGID[7]; 0012.00 char RESRVD; 0013.00 char EXCPDATA[100]; 0014.00 } ERRSTRUCTURE; /* Define the error return structure */ 0015.00 ERRSTRUCTURE errcode;/* Error Code Structure for RCVMSG */
として定義しておいて
0034.00 errcode.BYTESPRO = 160; 0035.00 errcode.BYTESAVL = 0;
と設定しておいてから API を実行するとエラーがなく正常に実行が完了した場合は
errcode.BYTESAVL は 0 のままであるが、エラーの場合はここに値が入る。
エラーの場合は errcode.MSGID には CPFメッセージの メッセージ識別コードが入り、
errcode.EXCPDATA にはメッセージ・データが入るので
API : QMHRTVM を使えばデータを埋め込んだエラー・メッセージを取得することができる。
API でエラーが発生したときにエラー・メッセージやエラー行を取り出すには
ざっと次の ApiError 関数のような感じになる。
0034.00 #include <QMHRTVM.h> : : 0037.00 QWCRSVAL(&sysval, sizeof(SYSVAL), 1, "QSRLNBR ", &errcode); 0292.00 if(errcode.BYTESAVL != 0){/* APIERR */ 0293.00 ApiError("QWCRSVAL", __LINE__, &errcode, "VERINFO"); return FALSE; 0294.00 }/* APIERR */ : : 0477.00 /*********************************************************************/ 0478.00 void ApiError(char* place, int stmno, ERRSTRUCTURE* errcode, char* pgm) 0479.00 /*********************************************************************/ 0480.00 { 0481.00 char msgid[8], msgdta[101], Message[512], sndmsg[132]; 0482.00 int msglen, msgdtalen, pos, strpos, len; 0483.00 char* ptr; 0484.00 typedef struct { 0485.00 Qmh_Rtvm_RTVM0100_t rtvm0100; 0486.00 char msg[512]; 0487.00 } ERRMSG; 0488.00 ERRMSG errmsg; 0489.00 char EBC_CRLF[] = {0x40, 0x15, 0x00}; 0490.00 0491.00 memset(msgid, 0, sizeof(msgid)); 0492.00 memcpy(msgid, errcode->MSGID, 7); 0493.00 msgid[7] = 0x00; 0494.00 memset(msgdta, 0, sizeof(msgdta)); 0495.00 memcpy(msgdta, errcode->EXCPDATA, 100); 0496.00 msgdta[100] = 0x00; 0497.00 msglen = sizeof(ERRMSG); 0498.00 msgdtalen = strlen(msgdta); 0499.00 memset(&errmsg, 0, sizeof(ERRMSG)); 0500.00 QMHRTVM(&errmsg, msglen, "RTVM0100", msgid, "QCPFMSG *LIBL ", 0501.00 msgdta, msgdtalen, "*YES ", "*YES ", errcode); 0502.00 memset(Message, 0, sizeof(Message)); 0503.00 memcpy(Message, errmsg.msg, 512); 0504.00 strpos = 0; 0505.00 pos = strlen(Message); 0506.00 ptr = strstr(Message, "&N"); 0507.00 while(ptr != NULL){/* while */ 0508.00 pos = (int)(ptr - Message); 0509.00 len = pos - strpos; 0510.00 memcpy(sndmsg, &Message[strpos], len); 0511.00 sndmsg[len] = 0x00; 0512.00 SendErrXML(sndmsg, FALSE); 0513.00 memcpy(&Message[pos], EBC_CRLF, 2); 0514.00 ptr = strstr(&Message[pos], "&N"); 0515.00 }/* while */ 0516.00 len = pos - strpos; 0517.00 memcpy(sndmsg, &Message[strpos], len); 0518.00 sndmsg[len] = 0x00; 0519.00 SendErrXML(sndmsg, TRUE); 0520.00 }
さて話を元に戻して
0043.00 offset = sysval.offset - 8; 0044.00 table = (TABLE*)&(sysval.value[offset]); 0045.00 memcpy(QSRLNBR, table->data, 8);
のようにして offset ( オフセット ) を sysval.offset から 8 を引いた値にセットしてから
table = (TABLE*)&(sysval.value[offset]);
のようにしてポインター定義である table に重ね合わせて解釈するようにしている。
8 を引いた値が正しいオフセットになるというのは実験したからであり IBM マニュアルには
どこにも書かれてはいない。
このように API の場合はマニュアルだけではなく実際に小さなプログラムを書いて
実験してみることが必要になってくる。
最初に少し癖のある API : QWCRSVAL を紹介したが、これで C/400 で自由にシステム値を
検索することができるようになるはずである。
さらに追記として API のレシーバー変数は i5/OS のリリース・アップに伴って予告なく拡張される。
API によってはレシーバー変数の長さの分しか値を戻さないのもあるが、ものによっては
長さが不足である、というエラーとなって動作しなくなる API も存在する。
従って i5/OS がリリース・アップされた場合は API に関しては 上位互換は保証されていない。
元々 IBM 社内の開発用のプログラムをユーザーにも公開したのが API であるから
通常の製品のコマンドのようにユーザーにはやさしくはないのだ。
厳密に言えばレシーバー変数は将来のために現行より余分を見て長くしておけば
このような拡張にも対応が可能となる。
経験豊かなプログラマーであれば OS のリリース・アップへの考慮も必要であるし
OS のリリース・アップがあれば十分テストくらいはしてください、というところだろうか?
確かにそれは当然のことである。