先の章ではILE のサービス・プログラム(*SRVPGM
) とは Windows の DLL のようなものであると
説明したが、もう少し正確に言えば、Windows の OLE オートメーション・サーバー(dll) であると言える。
Chicago のExcelサーバー(ExcelServer.dll
) は、まさに OLEオートメーション・サーバーである。
OLE オートメーション・サーバーとは Microsoft の VisualStudio の生成で OLEオートメーション・サーバー
の生成を指示すると、その新規に生成した OLE オートメーション・サーバー(dll) には
Microsoft が世界中で絶対に重複しないと保証して断言している個別体を識別するための識別子が
自動生成される。
ExcelServer.dll の場合では、
uuid(928232CD-1A98-4733-94E8-B7E9D295FD25)
という識別子であり、これがレジストリに登録される。
レジストリの同じ場所には、この ExcelServer.dll
が、実際に保管されている場所が登録されている。
Windows は、最初に起動したときに、この識別子を覚えておいて、次にExcelServer.dll
が
要求されたときにはこの識別子を使って、ExcelServer.dll
の実際のありかを発見して
ロードするというわけである。
やたらPCにフリー・ソフトを導入すると Windows の起動が遅くなったり、新しいAppを導入すると
Windows の再起動が要求されるのは、このためである。
(この仕組みから言うと新規のAppを導入しても、Windows の再起動する必要はない。
再起動を要求するのはレジストリから正しく読み出せるかをユーザーに確認させるためだけのものである。)
Windows の OLE オートメーション DLL の話を長々としたのは、サービス・プログラムもまさしく、
OLE オートメーション DLL と、ほぼ同じ仕組みで動作しているからである。
この識別記号の理解がサービス・プログラムの作成においても重要であるからだ。
後で説明するがサービス・プログラムも、その個体を識別するための記号を持っている。
さて、サービス・プログラム(*SRVPGM
) の目的とは、
複数のプログラムで利用可能な公開関数(公開プロシージャー) を作成することである。
ここで初めて「プロシージャー」という概念が出てきたので解説する。
プロシージャーとは、パラメータと戻り値を持つ、いわば関数である。
プロシージャーの具体的な例は後に挙げるとして、プロシージャーとサブルーチンは良く似ている。
ある特定の機能だけを実行させるのにサブルーチンは、良く利用されるが
サブ・ルーチンはあくまでも、プログラムの一部であり、サブルーチンの中で利用される変数は
メイン・ルーチンの変数と共有している。
これに対してプロシージャーとは VisualBASIC, VC++, VBA などに使われる関数と同じものであり、
プロシージャー内で定義される変数は、そのプロシージャー内においてのみ有効である。
つまりサブルーチンがあくまでもプログラム・フローの一部であるのに対して、プロシージャーは
関数として中は完全にカプセル化して独立している。
このような関数の概念は OLE が公表されるまで、RPG の世界には存在しなかったものである。
サービス・プログラム(*SRVPGM
) には
公開プロシージャー @ 公開プロシージャー A : : 公開プロシージャー N
のように複数のプロシージャーが定義されて、いくつかが公開(EXPORT) 用として定義されて
上位のプログラムまたはサービス・プログラムから関数を利用できるようになっている。
それではサンプルとして品種マスター(HINSHU) を検索して品種名を戻り値として戻す
サービス・プログラム(*SRVPGM
) の例を紹介しよう。
--------------------------------------------------------------------------------- 0001.00 H NOMAIN 0002.00 F********** 品種マスターの検索 *************************************** 0003.00 FHINSHU IF E K DISK EXTFILE(HINSHU_LIB) 0004.00 F********************************************************************** 0005.00 D HINSHU_LIB S 13 INZ('QTRFIL/HINSHU') 0006.00 **************************************************** 0007.00 * プロシージャーのプロトタイプ宣言 * 0008.00 **************************************************** 0009.00 D*( RTV_HSNAME のプロトタイプ宣言 ) 0010.00 D RTV_HSNAME PR 14A 0011.00 D HNSCOD 4A Value 0012.00 0013.00 ************************************************************ 0014.00 * RTV_HSNAME : 品種名の検索 0015.00 ************************************************************ 0016.00 *---( RTV_HSNAME PROCEDURE ここから )---------------------* 0017.00 P RTV_HSNAME B EXPORT 0018.00 D RTV_HSNAME PI 14A 0019.00 D HNSCOD 4A Value 0020.00 C MOVE *BLANKS HNSNAM 0021.00 C SETOFF 99 0022.00 C HNSCOD CHAIN HINSHU 99 0023.00 C RETURN HNSNAM 0024.00 P RTV_HSNAME E 0025.00 *---( RTV_HSNAME PROCEDURE ここまで )---------------------* ---------------------------------------------------------------------------------
@プロシージャー : RTV_HSNAME
のRPGソースの作成
ここでは「RTV_HSNAME
」という名前の 公開プロシージャーを作成する。
ひとつのサービス・プログラムの中には、複数個のプロシージャーを、いくつも定義することが
できるが、ここではサンプルとして、「RTV_HSNAME
」という,ひとつだけのプロシージャーを
定義している。
H NOMAIN
は、これがメイン・ルーチンを含んでないことを宣言している。
H NOMAIN
がないと、プログラムの終了方法が不明であるとの由のコンパイル・エラーが
発生する。
プロシージャーの呼び出しには、一般には
CALLB と CALLP
による方法があるが、CALLB
は、プロトタイプの宣言を必要としないが、CALLP
は
プロシージャーを呼び出す前には、プロトタイプの宣言が必要となる。
しかし EVAL
やフリーフォーマット形式での記述は、CALLP
に限られてくるので
最初から CALLP
だけを利用するようにすべきであろう。
ここでは最初に「RTV_HSNAME
」のプロトタイプを宣言する。
0010.00 D RTV_HSNAME PR 14A 0011.00 D HNSCOD 4A Value
PR
が、これがプロシージャーのプロトタイプであることを示しているのと同時に
14A
が 14バイトの文字を戻り値として宣言している。
Value
で示されている 4A
の HNSCOD
がパラメータである。
つまり、品種コード HNSCOD
を入力値として受け取って、14A
の品種名を戻す
プロシージャーとして宣言している。
次にプロシージャー RTV_HSNAME
の本体の定義が
0017.00 P RTV_HSNAME B EXPORT 0018.00 D RTV_HSNAME PI 14A 0019.00 D HNSCOD 4A Value : : 0024.00 P RTV_HSNAME E
プロシージャーは、このようにして
RTV_HSNAME B (BEGINEの意味) : RTV_HSNAME E (END の意味)
に囲まれたあいだに定義を記述する。
プロシージャーの演算の中だけに定義したい変数があれば、
0019.00 D HNSCOD 4A Value
の直後に、記述すればよい。
このようなプロシージャー内に定義される変数は「ローカル変数
」と呼ばれて
プロシージャー内においてのみ有効である。
これに対してプロシージャーの外に定義された変数は、このソースのすべての
プロシージャーから参照したり利用することができる。
このようにサービス・プログラム全体に共通して利用できる変数のことを
「グローバル変数
」と呼ぶ。
他のプロシージャーで利用することがないのであれば、できるだけ
ローカル変数
として定義すべきである。
ローカル変数
だけの利用であれば、プロシージャーは完全にカプセル化することが
できる。このような独立してカプセル化された演算を記述することができるのが
プロシージャーであり、従来のサブルーチンが、すべてグローバル変数
しか扱えないのとは
大きく異なる点である。
Aサービス・プログラムからモジュールを作成する。
それでは上記のRPGソースからモジュール(*MODULE
) を作成してみよう。
CRTRPGMOD MODULE(QTEMP/SMP002) SRCFILE(MYSRCLIB/QRPGLESRC) DBGVIEW(*SOURCE) AUT(*ALL)
によってモジュール(*MODULE
) をライブラリー QTEMP に作成する。
QTEMP を指定しているのはモジュールはコンパイルのための一時的なオブジェクトであるため
目的とするサービス・プログラム(*SRVPGM
) が作成されてしまえば、もはやモジュールは
不要であるからである。
B EXPORT ファイルを記述する。
プロシージャーの公開プロシージャーを宣言するための EXPORT
ファイルを
ソース・ファイル : QSRVSRC
として次のように作成しておく。
CRTSRCPF FILE(MYSRCLIB/QSRVSRC) RCDLEN(92) IGCDTA(*YES) CCSID(65535) AUT(*ALL)
これにまず最初に次のように記述する。
------------------------------------------------------------ 0001.00 STRPGMEXP PGMLVL(*CURRENT) 0002.00 EXPORT SYMBOL("RTV_HSNAME") 0003.00 ENDPGMEXP ------------------------------------------------------------
最初は PGMLVL(*CURRENT)
として EXPORT
ファイルを定義して、このEXPORT
ファイルによって
サービス・プログラムを作成すると、最初に長々と説明した,このサービス・プログラムの個体を
識別する「識別子」が OS400 によって自動生成される。
生成された「識別子」は、恐らくは世界中で重複することはないだろう。
C サービス・プログラムを作成する。
ここでサービス・プログラムを次のようにして作成する。
CRTSRVPGM SRVPGM(MYOBJLIB/SMP002) MODULE(QTEMP/SMP002) SRCFILE(MYSRCLIB/QSRVSRC) AUT(*ALL)
によってサービス・プログラムが作成される。
サービス・プログラムを DSPSRVPGM
コマンドによって参照すると、次のような画面を
表示することができる。
D EXPORT ファイルを修正する。
サービス・プログラムが作成できたからといって、実はこれで終わりではない。
最初の EXPORT
ファィルで
STRPGMEXP PGMLVL(*CURRENT)
として定義していたことを思い出して欲しい。このままでは、記号を毎回、自動生成することを
意味しているのである。
つまり、このままでは、このサービス・プログラムは、再コンパイルの度に、新たな記号が
再割り振りされるのである。
このサービス・プログラムをバインドしている上位のプログラムがあれば、それは
サービス・プログラムが再コンパイルされる都度、上位のプログラムも再コンパイルしないと
上位のプログラムはサービス・プログラムの記号を参照しているのであるから
再コンパイルのないままでは実行時にサービス・プログラムを呼び出すことができなくなってしまう。
そこで、DSPSRVPGM
で、このサービス・プログラムの記号をコピーしておいてから
EXPORT
ファイルを次のように修正するのである。
0001.00 STRPGMEXP PGMLVL(*CURRENT) + 0002.00 SIGNATURE(X'000000000000C5D4C1D5E2C86DE5E3D+ 0003.00 9') 0004.00 EXPORT SYMBOL("RTV_HSNAME") 0005.00 ENDPGMEXP
この修正によって サービス・プログラムの記号は固定される。
E サービス・プログラムを再作成する。
先に行った同じコマンドで サービス・プログラムを次のように再作成する。
CRTSRVPGM SRVPGM(MYOBJLIB/SMP002) MODULE(QTEMP/SMP002) SRCFILE(MYSRCLIB/QSRVSRC) AUT(*ALL)
このようにして、サービス・プログラムの記号を固定してしまえばサービス・プログラムに
変更があってサービス・プログラムを再コンパイルしたとしても、上位のプログラムは一切、
再コンパイルする必要はない。
サービス・プログラムのプロシージャーがたとえ、増えたとしても最後尾に
追加のプロシージャーを定義するのであれば、上位プログラムはやはり
再コンパイルは必要でない。
このように記号の固定化を、しっかり行っておくことによってサービス・プログラムの
オブジェクト指向を十分、生かすことができる。
このことは IBM マニュアルでも、ことさら強調されているわけではないので
注意を喚起する意味において解説を行った。