メニューボタン
IBMi海外記事2022.11.09

SND-MSG命令コードとメッセージ サブファイル

Ted Holt 著

もしあなたが、今もグリーンスクリーン アプリケーションを書いている大勢のIBM i プログラマーのうちの1人であるとしたら、あなたが書くプログラムが、ユーザーとどのようにコミュニケーションを取っているか考えてみてください。ユーザーに対して、今、入力した値が無効だということや、先に進むにはコマンド キーを押す必要があるということを、どのようにして伝えているでしょうか。いくつかの方法を目にしてきましたが、よく用いられている方法(私のお気に入りでもあります)は、メッセージ サブファイルを通じてコミュニケーションする方法です。

メッセージ サブファイルの素晴らしいところは、一度に複数のメッセージを伝えられる点です。私は、コンピューターにできるだけ多くのエラーを見つけてもらって、ユーザーがそれらをすべて処理してから再試行できるようにしたいと思います。一度に1つのエラーを修正するように求められると、そのアプリケーション(さらにはWebページも)を使用するのが不愉快になってしまいます。しかし、そのように動作するグリーンスクリーン アプリケーションを目にしたことがあります。あるいは、私もいくつか書いたことがあるかもしれません。

この記事に含まれているコードはこちらからダウンロードできます。

プログラムでメッセージ サブファイルを使用するには、表示装置ファイルでメッセージ サブファイルと、対応するサブファイル制御レコードを定義する必要があります。以下は、私がいつも使用しているソース コードです。唯一、変更するのは、SFLMSGRCDキーワードの値であり、これは、画面サイズ(24×80または27×132)あるいはウィンドウ サイズに応じて変更します。

     A          R MSGSFL                    SFL
     A                                      SFLMSGRCD(24)
     A            MSGKEY                    SFLMSGKEY
     A            PGMNAME                   SFLPGMQ
     A          R MSGCTL                    SFLCTL(MSGSFL)
     A                                      OVERLAY
     A                                      SFLDSP
     A                                      SFLDSPCTL
     A                                      SFLINZ
     A N99                                  SFLEND
     A                                      SFLSIZ(10)
     A                                      SFLPAG(1)
     A            PGMNAME                   SFLPGMQ 

RPGプログラムは、以下の方法でメッセージ サブファイルと対話します。

  • プログラムが、プログラム メッセージ待ち行列に適切なメッセージを送信する。
  • ユーザーがそれらのメッセージを表示した後で、プログラムはプログラム メッセージ待ち行列をクリアする。

プログラム メッセージ待ち行列へメッセージを送る通常の方法は、「プログラム・メッセージの送信(Send Program Message)」(QMHSNDPM)APIを使用する方法です。ユーザーにメッセージが表示され続けないように、メッセージを削除するには、「プログラム・メッセージの削除(Remove Program Messages)」(QMHRMVPM)APIを使用します。『IT Jungle』では、長年にわたって、こうしたAPIをテーマとした、かなりの数の記事を公開してきました。 こちらの記事 は、20年前のものです。ソース コードは旧式ですが、それでも根本部分は今でも通用します。

SND-MSG命令コードの導入により、IBMがこのプロセスをさらに簡単にしてくれたことを、先日知って嬉しく思いました。SND-MSGとQMHSNDPMは、ほぼ同じ処理を行いますが、SND-MSGの方が使いやすいと思います。IBMがそれと同等のRPGの機能を提供していないため、まだQMHRMVPMは必要です。

以下に、興味をお持ちであれば、コンパイルして試してみることができる例を示します。まず、以下は、表示装置ファイルTHQ0051DのDDSです。

     A                                      DSPSIZ(24 80 *DS3)
     A                                      CA03(03 'F3=EXIT')

     A          R REC1
     A                                      OVERLAY
     A                                  1 36'Data Entry'
     A                                  3  3'Fill in the blanks, press Enter.'
     A                                  5  3'Your name . . . . . :'
     A            NAME          24   B  5 25
     A  61                                  DSPATR(PC RI)
     A                                  7  3'Your age  . . . . . :'
     A            AGE            3  0B  7 25
     A  62                                  DSPATR(PC RI)
     A                                  9  3'Your address  . . . :'
     A            STREET        24   B  9 25
     A  63                                  DSPATR(PC RI)
     A                                 10  5'City, state, ZIP  :'
     A            CITY          16   B 10 25
     A  64                                  DSPATR(PC RI)
     A            STATE          2   B 10 43
     A  65                                  DSPATR(PC RI)
     A            ZIP            5  0B 10 47
     A  66                                  DSPATR(PC RI)
     A                                 23  5'F3=Exit'

     A          R MSGSFL                    SFL
     A                                      SFLMSGRCD(24)
     A            MSGKEY                    SFLMSGKEY
     A            PGMNAME                   SFLPGMQ

     A          R MSGCTL                    SFLCTL(MSGSFL)
     A                                      OVERLAY
     A                                      SFLDSP
     A                                      SFLDSPCTL
     A                                      SFLINZ
     A N99                                  SFLEND
     A                                      SFLSIZ(10)
     A                                      SFLPAG(1)
     A            PGMNAME                   SFLPGMQ 

REC1は、単純なデータ入力フォーマットです。ユーザーは、氏名、年齢、住所を入力します。

スクリーンショット

以下は、RPGプログラムTHQ0051Rです。画面の処理を行います。

**free
ctl-opt actgrp(*new) option(*srcstmt: *nodebugio);

dcl-f  THQ0051D   workstn;

dcl-ds  psds  psds;
   PgmName   char(10);
end-ds;

dcl-pr QMHRMVPM  extpgm;
   CallStackEntry    char(10)  const;
   CallStackCounter  int (10)  const;
   MessageKey        char( 4)  const;
   MessagesToRemove  char(10)  const;
   ErrorCode         int (10)  const;
end-pr QMHRMVPM;

dcl-s  DataIsValid    ind;

*inlr = *on;

dou *in03
 or DataIsValid;
   write MsgCtl;
   exfmt Rec1;
   QMHRmvPM (PgmName: *zero: *blanks: '*ALL': *zero);
   if not *in03;
      exsr Validate;
   endif;
enddo;

// Do something with the data

return;

begsr Validate;

   %subarr(*in: 61: 6) = *all'0';

   if Name = *blanks;
      *in61 = *on;
      Snd-Msg 'Your name is required.';
   endif;

   if Age < 18;
      *in62 = *on;
      Snd-Msg 'Age must be 18 or greater.';
   endif;

   if not (State in %list('TX': 'OK': 'LA'));
      *in65 = *on;
      Snd-Msg 'The state "' + State + '" is not authorized.';
   endif;

   // Similar code to validate the other fields.

   DataIsValid = not (   *in61 or *in62 or *in63
                      or *in64 or *in65 or *in66 );

endsr; 

Validateサブルーチンに注目してください。即時通知メッセージをプログラム メッセージ待ち行列へ送信する、SND-MSG命令の簡単さにお気付きでしょうか。これより簡単な方法を私は知りません。

例を分かりやすくしたいという思いから、DDSおよびRPGソース コードは、短く、単純にしています。このコードを本番用に書くとしたら、以下のように、いくつか機能強化を行うでしょう。

  • コンパイラーがグローバル変数をフィールドに割り当てないように、 表示装置ファイルを修飾 する。
  • グローバルなデータを使用する代わりに、パラメーターを通じてデータを渡すことができるように、データを検証するのにサブルーチンではなくサブプロシージャーを使用する。
  • グローバルなキャッチオール モニター を追加して、ユーザーが「プログラム・メッセージの表示(Display Program Messages)」画面を表示することのないようにする。幸いにも、この状況で有用な「エスケープ・メッセージの再送信(Resend Escape Message)」(QMHRSNEM)APIがIBMから提供されている。

以下は、同じアプリケーションに対して、少し機能強化を行ったものです。表示装置ファイルで変更が必要なのは1か所のみです。すなわち、RPGプログラムでファイルを修飾するには、表示装置ファイルのDDSにINDARAキーワードを指定する必要があります。

A                                      DSPSIZ(24 80 *DS3)
A                                      INDARA            
A                                      CA03(03 'F3=EXIT')
 . . . and so on . . .

以下は、前述の変更を行ったRPGプログラムです。

**free
ctl-opt main(THQ0054R) actgrp(*new) option(*srcstmt: *nodebugio);

dcl-f  Display    workstn   qualified
                            extdesc('THQ0052D') extfile(*extdesc)
                            alias
                            indds(wsi)
                            usropn;

dcl-ds  Rec1_t   LikeRec(Display.REC1  : *all)   template;
dcl-ds  MsgCtl_t LikeRec(Display.MsgCtl: *all)   template;

// Indicator data structure for display file
dcl-ds wsi             len(99)   qualified    inz;
   ExitRequested       ind       pos( 3);
   ErrorInds           char(6)   pos(61);
   ErrorInd_Name       ind    overlay(ErrorInds:  1);
   ErrorInd_Age        ind    overlay(ErrorInds:  2);
   ErrorInd_Street     ind    overlay(ErrorInds:  3);
   ErrorInd_City       ind    overlay(ErrorInds:  4);
   ErrorInd_State      ind    overlay(ErrorInds:  5);
   ErrorInd_Zip        ind    overlay(ErrorInds:  6);
end-ds wsi;

dcl-ds  psds  psds     qualified;
   PgmName   char(10);
end-ds;

dcl-pr QMHRMVPM  extpgm;
   CallStackEntry    char(10)  const;
   CallStackCounter  int (10)  const;
   MessageKey        char( 4)  const;
   MessagesToRemove  char(10)  const;
   ErrorCode         int (10)  const;
end-pr QMHRMVPM;

dcl-pr QMHRSNEM extpgm;
   MessageKey        char( 4)   const;
   ErrorCode         int (10)   const;
end-pr QMHRSNEM;

dcl-proc THQ0054R;

   monitor;
      THQ0054R_Main ();
   on-error;
      QMHRsnEM (*blanks: *zero);
   endmon;

end-proc THQ0054R;

dcl-proc THQ0054R_Main;

   dcl-s  DataIsValid    ind;

   dcl-ds  Rec1_data   likeds(Rec1_t)   inz;
   dcl-ds  MsgCtl_Data likeds(MsgCtl_t) inz;


   open Display;

   dou wsi.ExitRequested
    or DataIsValid;
      MsgCtl_data.PgmName = psds.PgmName;
      write Display.MsgCtl MsgCtl_data;
      exfmt Display.Rec1   Rec1_data;
      QMHRmvPM (psds.PgmName: *zero: *blanks: '*ALL': *zero);
      if not wsi.ExitRequested;
         Validate (Rec1_data: DataIsValid);
      endif;
   enddo;

   // Do something with the data

   close Display;

end-proc THQ0054R_Main;

dcl-proc Validate;

   dcl-pi *n;
      inRec1_data      likeds(Rec1_t)   const;
      ouDataIsValid    ind;
   end-pi;

   wsi.ErrorInds = *zeros;

   if inRec1_data.Name = *blanks;
      wsi.ErrorInd_Name = *on;
      Snd-Msg 'Your name is required.' %target(psds.PgmName);
   endif;

   if inRec1_data.Age < 18;
      wsi.ErrorInd_Age = *on;
      Snd-Msg 'Age must be 18 or greater.' %target(psds.PgmName);
   endif;

   if not (inRec1_data.State in %list('TX': 'OK': 'LA'));
      wsi.ErrorInd_State = *on;
      Snd-Msg ('The state "' + inRec1_data.State + '" is not authorized.')
               %target(psds.PgmName);
   endif;

   // Similar code to validate the other fields.

   ouDataIsValid = (wsi.ErrorInds = *zeros);

end-proc Validate; 

このコードのすべてを説明する必要はないと思いますが、SND-MSG命令に対する変更については指摘しておく必要があるでしょう。それがこの記事のテーマだからです。SND-MSGがサブプロシージャー内にあるため、%TARGETキーワードを使用して、プログラム メッセージ待ち行列の名前を指定する必要があります。

Snd-Msg 'Your name is required.' %target(psds.PgmName);

また、RPGプログラムは、メッセージ サブファイル レコードそのものではなく、メッセージ サブファイル制御レコードを使用することについても指摘しておきたいと思います。これは、表示装置ファイルが修飾されているかどうかにかかわらず、そうなります。

コミュニケーション能力というのは、人生のあらゆる場面で重要なものです。とは言っても、話し言葉であれ、書き言葉であれ、うまくコミュニケーションを取る能力が身に着いている人が多くないというのは実に残念なことです。しかし、幸いなことに、グリーンスクリーン プログラムにユーザーとコミュニケーションを取らせることは難しいことではありません。そして、SND-MSG命令コードが加わったことで、これまで以上に簡単になっています。

あわせて読みたい記事

PAGE TOP