API に何か値を問い合わせて結果としての値を受け取る、というケースは非常に多い。
むしろ結果を求めるために API を起動しているのであるから、これは至極当然な話である。
しかし API によっては結果の情報量で莫大であり、時には十分であったと
予想していた結果の変数の長さをオーバーしてしまう場合がある。
しかし API は良くしていたものでパラメータとして
- 結果の変数
- 左の結果の変数の長さ
を対にしてパラメータとして渡すケースが多い。
このとき API は真の実際の結果が受け取り変数の長さをオーバーするようなことが
あっても結果の変数の長さが不足している旨のエラー・メッセージは出力せずに
与えられた結果の変数の長さの分だけを結果として戻すようになっている場合が多い。
エラーにはならないのでAPIが正常に終了したものと思い込んで処理を進めていくと
やがては情報が不足していることに気づくことになってしまう。
このようなことにならないためには結果の変数の長さを大きいものにしなければ
ならないのだろうか ?
API によってはヒープ・サイズに近いメモリを用意しなければならないものもあるので
毎回、そのような莫大な変数を用意するのは全く、無駄と言える。
特に問題となるのは DSPF の構造を調べる API : QDFRTVFD である。
API : QDFRTVFD (表示装置ファイル記述の検索) は
必須パラメータ・グループ:
1. | レシーバー変数 | 出力 | Char(*) |
2. | レシーバー変数の長さ | 入力 | Binary(4) |
3. | 形式名 | 入力 | Char(8) |
4. | 修飾ファイル名 | 入力 | Char(20) |
5. | エラー・コード | 出力 | Char(*) |
と定義されているので受け取り変数とその長さを初めに指定しておかなければならない。
しかし修飾ファイル名として与える DSPF によっては結果としての変数は大きくもなれば
小さい場合もあるはずだ。
適当な値を指定しておいてもある程度のケースではうまく処理されたとしても
例外的に大きな DSPF が指定された場合はレシーバー変数の長さをオーバーしてしまうことも
理論的にありうる。
社内処理であれば大多数のケースが成功したとしても不特定多数の提供することを
前提としているソフトウェア・パッケージの場合は、そうはいかない。
すべてのケースを前提として考えなければならないからである。
そこでここではあらゆるレシーバー変数の長さにも対応できるような手法を紹介する。
結論から説明すると方法としては
- まず受け取り変数の長さを調べてから
- その長さの分だけの変数を定義して API を実行する
という手法を採るのである。
そりゃあ必要な変数の長さがわかっていればそれに越したことはないしわかれば苦労しない、
ということになるので受け取り変数の長さをどのようにして知るのがが鍵になる。
最初にこの問題を解くヒントとなるのは
基本ファイル・セクション(QDFFBASE) の戻り変数の構造が次のようになっていることである。
オフセット | タイプ | 変数名 | フィールド | |
---|---|---|---|---|
10進数 | 16進数 | |||
0 |
0 |
BINARY(31) |
WDFFRETN |
返されるデータの長さ |
4 |
4 |
BINARY(31) |
WDFFSIZE |
表示装置ファイル記述のサイズ |
: |
最初の WDFFRETN には今、返された変数の長さが入っているが WDFFSIZEには
QDFFRTVFD の情報に必要なサイズが記述されているのである。
そしてこれが求める必要な変数のサイズである。
つまり、最初に QDFFBASE を調べて WDFFSIZE の値がわかれば、この値の変数を
改めて動的に定義してから API : QDFRTVFD を実行すればよいということになる。
実際の具体的なソースの例を次に示す。
【 サンプル・ソース 】
0174.00 /*************************************************************/ 0175.00 /* グ ロ ー バ ル 変 数 */ 0176.00 /*************************************************************/ 0177.00 int format_size; 0178.00 char* formatBuf; : 0212.00 QDFFBASE_t qdffbase_t; /* 長さの取得のためのヘッダー */ : 0220.00 /* QDFRTVFD : 表示装置ファイル記述の検索 */ 0221.00 errcode.BYTESPRO = 160; 0222.00 errcode.BYTESAVL = 0; 0223.00 0224.00 /* 2015/03/25: formatBuf を動的な確保に改訂した。 */ 0225.00 /*(1) 最初に formatBuf に必要な長さを取得する */ 0226.00 QDFRTVFD(&qdffbase_t, sizeof(QDFFBASE_t),"DSPF0100", DSPFFLIB, &errcode); 0227.00 if(errcode.BYTESAVL != 0){/* APIERR */ 0228.00 if(strncmp(errcode.MSGID, "CPF0679", 7) == 0){/* DSPF38 */ 0229.00 if((QDFRTVFD38(formatBuf, &format_size, "DSPF0100", DSPFFLIB, 0230.00 &errcode)) == FALSE){/* S38 でも失敗 */ 0231.00 return FALSE; 0232.00 }/* S38 でも失敗 */ 0233.00 }/* DSPF38 */ 0234.00 return FALSE; 0235.00 }/* APIERR */ 0236.00 /*(2) 割り振った長さの分の formatBuf を取得する */ 0237.00 else{/* 長さの取得は成功 */ 0238.00 /* format_size を動的に確保 */ 0239.00 format_size = qdffbase_t.WDFFSIZE; 0240.00 formatBuf = (char*)malloc(format_size + 1); 0241.00 QDFRTVFD((char*)formatBuf, format_size, "DSPF0100", DSPFFLIB, &errcode); 0242.00 if(errcode.BYTESAVL != 0){/* APIERR */ 0243.00 sprintf(point, "RTVDSPFKEYS-QDFRTVFD =[%s]", DSPFFLIB); 0244.00 errcode.RESRVD = '1'; 0245.00 return FALSE; 0246.00 }/* APIERR */ 0247.00 }/* 長さの取得は成功 */
【解説】
0226.00 QDFRTVFD(&qdffbase_t, sizeof(QDFFBASE_t),"DSPF0100", DSPFFLIB, &errcode);
によって qdffbase_t を取得して必要な長さを
0239.00 format_size = qdffbase_t.WDFFSIZE;
によって取得してから
0240.00 formatBuf = (char*)malloc(format_size + 1);
によって動的に変数 formatBuf を割り振ってから
0241.00 QDFRTVFD((char*)formatBuf, format_size, "DSPF0100", DSPFFLIB, &errcode);
を実行すれば必要な長さの分だけのメモリ・サイズで処理を行うことができるようになる。