RPG

142. ILE入門 (2)

■ サービス・プログラム(*SRVPGM) とは何か ?

先の章では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) の例を紹介しよう。

【 例:サービス・プログラム SMP002 】
---------------------------------------------------------------------------------
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 で示されている 4AHNSCOD がパラメータである。
つまり、品種コード 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 マニュアルでも、ことさら強調されているわけではないので
注意を喚起する意味において解説を行った。