API : ライブラリー関数とは ILE-RPGが実際に内部で行っている関数であり
ILE-RPGの演算命令を模擬実行することができる。
つまりILE-RPGの入出力演算命令は内部では「ライブラリー関数」と呼ばれるAPI
によって行われている。
_
弊社が現在、開発中のPythonフレーム・ワーク(Python.400)では
このことを利用してPythonでILE=RPGがやっているのと同じ入出力命令を
実行している。
しかしその中で印刷ファイルに出力するAPIは120バイト以上の出力を
行おうとすると不明なエラー・メッセージが頻出することがわかった。
この記事はこの障害の回避の方法を紹介するものである。
一般的には使われない非常に特殊な処理なので
興味のない方は読み飛ばして頂いてもかまわない。
_
IBM ライブラリー関数の詳細はここから
現在、開発中のPython.400でもPythonで RPGのWRITE命令を
実行するために使われている。
ライブラリー関数を組み合わせることによってRPGがやっている演算命令と
同じものをそっくりPythonでも実行することができる。
_Rwrite関数のIBM 解説はここから
_
RPGの代わりに C言語やRPGからでも _Rwriteを使って
RPG の WRITE命令と同じことを実行することができる。
具体的にには
① _Ropen関数でファイルをオープンして
ファイルへのポインタを取得しておく。
② _Rformat関数で出力しようとするファイルのレコード名を
宣言する。
つまり ILE-RPGの場合は WRITE 命令だけで
指定したレコードにデータを出力することができるが
ライブラリー関数を使う場合は _Rformat関数によって
レコード名を指定しなければならない。
③ _Rwrite関数でデータをファイル・ポインタをり指示して
出力する。
という3段階を経てRPGのWRITE命令と同じことを
実現することができる。
つまりILE-RPGのひとつの入出力命令がライブラリー関数のひとつと
1対1に対応しているわけではなくて
ILE-RPGのWRITE命令= (_Rformat関数) + (_Rwrite関数) という
組み合わせでILE-RPG関数の動作を実現している。
この方法によって例えば印刷ファイルにも同様に_Rwriteで出力することができる。
_
[例] 印刷ファイル(PSHOHIN2)に出力するTESTPRT2
001.00 H DFTNAME(TESTPRT2) DATEDIT(*YMD/) BNDDIR('QC2LE')
002.00 F********** TRNPRT2 _RWrite 印刷テスト *******************************
003.00 F*
004.00 F**********************************************************************
005.00
006.00 D*( _Ropen() レコード・ファイルをオープンして入出力操作を行う )
007.00 D Ropen PR * ExtProc('_Ropen')
008.00 D RFILE * VALUE OPTIONS(*STRING:*TRIM)
009.00 D MODE * Value OPTIONS(*STRING:*TRIM)
010.00
011.00 D*( _Rformat() - レコード・フォーマット名の設定 )
012.00 D Rformat PR * ExtProc('_Rformat')
013.00 D RFILE * Value
014.00 D FORMAT * Value
015.00
016.00 D*( _Rwrite() - 次のレコードの書き込み ) - WRITE
017.00 D Rwrite PR * ExtProc('_Rwrite')
018.00 D RFILE * Value
019.00 D RECORD * Value
020.00 D RCD_LEN 10I 0 Value
021.00
022.00 D*( _Rclose() - ファイルのクローズ ) - CLOSE
023.00 D Rclose PR 10I 0 ExtProc('_Rclose')
0024.00 D RFILE * Value
0025.00
0026.00 D C2M_MSG S 10I 0 IMPORT('_C2M_MSG')
0027.00 *( 作業変数 )
0028.00 D RFILE_P S * ファイル・ポインタ
0029.00 D RECORD# S 11A
0030.00 D NULL S 1A INZ(X'00')
0031.00 D OUTBUF S 1024A
0032.00 D OUTBUF_P S * INZ(%ADDR(OUTBUF))
0033.00 D OUTLEN S 10I 0 INZ(168)
0034.00
0035.00 C/FREE
0036.00 C2M_MSG = 0;
0037.00 RFILE_P = Ropen('PSHOHIN2': 'ar, indicators=y');
0038.00 RECORD# = 'PRTHEAD ' + NULL;
0039.00 Rformat(RFILE_P: %ADDR(RECORD#)); // レコード・フォーマット名の設定
0040.00 Rwrite(RFILE_P: OUTBUF_P: OUTLEN);
0041.00 Rclose(RFILE_P);
0042.00 *INLR = *ON;
0043.00 C/END-FREE
[解説]
先に説明したように
0037.00 RFILE_P = Ropen('PSHOHIN2': 'ar, indicators=y');
0038.00 RECORD# = 'PRTHEAD ' + NULL;
0039.00 Rformat(RFILE_P: %ADDR(RECORD#)); // レコード・フォーマット名の設定
0040.00 Rwrite(RFILE_P: OUTBUF_P: OUTLEN);
0041.00 Rclose(RFILE_P)
というのが
①_Ropen関数でファイルをオープンして
ファイルへのポインタを取得しておく。
0037.00 RFILE_P = Ropen('PSHOHIN2': 'ar, indicators=y');
② _Rformat関数で出力しようとするファイルのレコード名を
宣言する。
0038.00 RECORD# = 'PRTHEAD ' + NULL; 0039.00 Rformat(RFILE_P: %ADDR(RECORD#)); // レコード・フォーマット名の設定
③_Rwrite関数でデータをファイル・ポインタをり指示して
出力する。
0040.00 Rwrite(RFILE_P: OUTBUF_P: OUTLEN);
である。
ところがこの_Rwrite の実行は「レコード長が長すぎる」という不明な
メッセージが出力されてエラーとなってしまう。
レコード・バッファーは出力されるのだが出力直後にエラーとなって
終了してしまう。
これは同じ _Rwrite命令を次のように C言語で実行してみると
起こらない。
_
_Rwrite命令を使ったC言語の例
0001.00 /********************************************************************/ 0002.00 /* */ 0003.00 /* TESTWRT2 : 印刷ファイル出力 */ 0004.00 /* */ 0005.00 /* Office Quattro Co,.Ltd 2024/01/26 7:40:00 created */ 0006.00 /* */ 0007.00 /* */ 0008.00 /********************************************************************/ 0009.00 #pragma comment(COPYRIGHT, "Visual PANEL-WORKER (C) CopyRight 0010.00 Office Quattro.Corp. 2024- All right reserved. Users Restricted 0011.00 Rights - Use, duplication or disclosure restricted by Office Quattro 0012.00 Corp. Licenced Materials-Property of Office Quattro.") 0013.00 #include0014.00 #include 0015.00 #include 0016.00 #include /* triml */ 0017.00 #include 0018.00 #include 0019.00 #include 0020.00 #include /* ropen(). rreadk() */ 0021.00 0022.00 #define TRUE 0 0023.00 #define FALSE -1 0024.00 int bLR = FALSE; 0025.00 typedef struct { 0026.00 int BYTESPRO; 0027.00 int BYTESAVL; 0028.00 char MSGID[7]; 0029.00 char RESRVD; 0030.00 char EXCPDATA[100]; 0031.00 } ERRSTRUCTURE; /* Define the error return structure */ 0032.00 ERRSTRUCTURE errcode;/* Error Code Structure for RCVMSG */ 0033.00 volatile _INTRPT_Hndlr_Parms_T ca; 0034.00 0035.00 /*************************************************************/ 0036.00 /* 内 部 使 用 関 数 */ 0037.00 /*************************************************************/ 0038.00 void GetParam(int argc, char *argv[]); 0039.00 void INZSR(void); 0040.00 void ApiError(char* place, int stmno, ERRSTRUCTURE* errcode, char* pgm); 0041.00 void LRRTN(char *argv[]); 0042.00 0043.00 /*************************************************************/ 0044.00 /* IMPORT 関 数 */ 0045.00 /*************************************************************/ 0046.00 /*************************************************************/ 0047.00 /* IMPORT 変 数 */ 0048.00 /*************************************************************/ 0049.00 /*************************************************************/ 0050.00 /* 外 部 呼 出 し 関 数 */ 0051.00 /*************************************************************/ 0052.00 void MonitorMSG(_INTRPT_Hndlr_Parms_T ca, char* ref); 0053.00 #pragma linkage(MonitorMSG, OS) 0054.00 #pragma map(MonitorMSG, "ASNET.COM/MONMSG") 0055.00 /*************************************************************/ 0056.00 /* グ ロ ー バ ル 変 数 */ 0057.00 /*************************************************************/ 0058.00 /*------( 受取りパラメータ値 )----------*/ 0059.00 char DATA[2049], TOFILLIB[22], ERR[1], MSG[133]; 0060.00 /*------( 受取りパラメータ値 )----------*/ 0061.00 char ref[133], MSG[133], ERR[1]; 0062.00 /********************************************************************/ 0063.00 /* m a i n --- main module of this pgm */ 0064.00 /* */ 0065.00 /* 1. DATA[2048] */ 0066.00 /* 2. TOFILLIB[21] */ 0067.00 /* 3. ERR[1] */ 0068.00 /* 4. MSG[132] */ 0069.00 /* */ 0070.00 /*------------------------------------------------------------------*/ 0071.00 0072.00 int main(int argc, char *argv[]){ 0073.00 _RFILE *fp = NULL; 0074.00 _XXOPFB_T *opfb; 0075.00 int RECL; 0076.00 0077.00 #pragma exception_handler(MONMSG, ca, 0, _C2_MH_ESCAPE, _CTLA_HANDLE) 0078.00 /* GetParam(argc, argv); *[ パラメータの取得 ]*/ 0079.00 INZSR(); /*[ 初期設定 ]*/ 0080.00 0081.00 strcpy(TOFILLIB, "QTROBJ/PSHOHIN2"); 0082.00 fp = _Ropen(TOFILLIB, "ar+"); 0083.00 opfb = _Ropnfbk ( fp ); 0084.00 _Rformat ( fp,"PRTHEAD" ); /* Set the record format for the */ 0085.00 //RECL = opfb->pgm_record_len + 12; 0086.00 RECL = 168; 0087.00 _Rwrite(fp, DATA, RECL); 0088.00 _Rclose(fp); 0089.00 0090.00 LRRTN(argv); 0091.00 exit(0); 0092.00 0093.00 MONMSG: 0094.00 #pragma disable_handler 0095.00 strcpy(ref, "CPYDTA"); 0096.00 MonitorMSG(ca, ref); 0097.00 0098.00 exit(0); 0099.00 } 0100.00 /*************************************/ 0101.00 void GetParam(int argc, char *argv[]) 0102.00 /*************************************/ 0103.00 { 0104.00 int len; 0105.00 0106.00 memcpy(DATA, argv[1], 2048); 0107.00 DATA[2048] = 0x00; 0108.00 memcpy(TOFILLIB, argv[2], 21); 0109.00 TOFILLIB[21] = 0x00; 0110.00 len = triml(TOFILLIB, ' '); 0111.00 if(len > 0) TOFILLIB[len] = 0x00; 0112.00 ERR[0] = ' '; 0113.00 memset(MSG, ' ', 132); 0114.00 } 0115.00 /****************/ 0116.00 void INZSR(void) 0117.00 /****************/ 0118.00 { 0119.00 errcode.BYTESPRO = 160; 0120.00 errcode.BYTESAVL = 0; 0121.00 ERR[0] = ' '; 0122.00 memset(MSG, ' ', 132); 0123.00 } 0124.00 /*********************************************************************/ 0125.00 void ApiError(char* place, int stmno, ERRSTRUCTURE* errcode, char* pgm) 0126.00 /*********************************************************************/ 0127.00 { 0128.00 char msgid[8], msgdta[101], Message[512]; 0129.00 int msglen, msgdtalen, pos; 0130.00 char* ptr; 0131.00 typedef struct { 0132.00 Qmh_Rtvm_RTVM0100_t rtvm0100; 0133.00 char msg[512]; 0134.00 } ERRMSG; 0135.00 ERRMSG errmsg; 0136.00 0137.00 memset(msgid, 0, sizeof(msgid)); 0138.00 memcpy(msgid, errcode->MSGID, 7); 0139.00 msgid[7] = 0x00; 0140.00 memset(msgdta, 0, sizeof(msgdta)); 0141.00 memcpy(msgdta, errcode->EXCPDATA, 100); 0142.00 msgdta[100] = 0x00; 0144.00 msgdtalen = strlen(msgdta); 0145.00 memset(&errmsg, 0, sizeof(ERRMSG)); 0146.00 QMHRTVM(&errmsg, msglen, "RTVM0100", msgid, "QCPFMSG *LIBL ", 0147.00 msgdta, msgdtalen, "*YES ", "*YES ", errcode); 0148.00 memset(Message, 0, sizeof(Message)); 0149.00 memcpy(Message, errmsg.msg, 512); 0150.00 ptr = strstr(Message, "&N"); 0151.00 if(ptr != NULL){ 0152.00 pos = (int)(ptr - Message); 0153.00 Message[pos] = 0x00; 0154.00 } 0155.00 printf("(%s) [ERR AT = %d] %s-%sn", place, stmno, msgid, Message); 0156.00 getchar(); 0157.00 exit(-1); 0158.00 } 0159.00 /************************/ 0160.00 void LRRTN(char *argv[]) 0161.00 /************************/ 0162.00 { 0163.00 if(bLR == TRUE) return; 0164.00 bLR = TRUE; 0165.00 memcpy(argv[3], ERR, 1); 0166.00 memcpy(argv[4], MSG, 132); 0167.00 exit(0); 0168.00 }
[解説]
これは社内用のプログラムなので冗長にはなっているが
本質の部分は
0081.00 strcpy(TOFILLIB, "QTROBJ/PSHOHIN2"); 0082.00 fp = _Ropen(TOFILLIB, "ar+"); 0083.00 opfb = _Ropnfbk ( fp ); 0084.00 _Rformat ( fp,"PRTHEAD" ); /* Set the record format for the */ 0085.00 //RECL = opfb->pgm_record_len + 12; 0086.00 RECL = 168; 0087.00 _Rwrite(fp, DATA, RECL); 0088.00 _Rclose(fp);
であり問題の部分は
0087.00 _Rwrite(fp, DATA, RECL);
であるがエラーなく実行される。
0086.00 RECL = 168;
としているが RPGの場合も
0033.00 D OUTLEN S 10I 0 INZ(168)
としているが実験してみると OUTLEN(=出力するレコード長)が
80バイト以下なら正常に動作するが 80バイトを超えると
「レコード長が長すぎる」という申告なエラーが発生してしまう。
しかし先のTESTPRT2は正常に動作できている。
理由は
0026.00 D C2M_MSG S 10I 0 IMPORT('_C2M_MSG')
:
0036.00 C2M_MSG = 0;
によって特殊なフィールド値: _C2M_MSG を 0 にセットしておくと
このエラー・メッセージを抑制することができると
次の説明に書かれている。
これによって入出力エラーを抑制することができたのだが
このエラーはどう見ても不正なエラーであって IBM のバグとしか思えない。
_Rwrite関数にこの説明があってよさそうなものであるが
どこにもそのような記述はない。
今後、_Rwrite関数を試してみようという人は少ないかも知れないが
弊社の備忘録とするとともに問題でもあるので解決方法として公開した。
_
