RPGやCOBOLなど、IBM i アプリケーションを開発する際に、
- あるファイルのレイアウトを変更したが、
どのプログラムがそのファイルを使用しているか(リコンパイルが必要か) - あるプログラムを改修したが、
どのプログラムが、改修したプログラムをCALLしているか 
などを調べたい局面があるかと思います。
Delphi/400でこれらのIBM i ファイルやプログラムを参照することもあるでしょう。
このような場合には、IBM i のDSPPGMREFコマンドを使用すると、
それらを一括で調査することが可能です。
今回は、DSPPFMREFコマンドの基本的な使用方法と、
このコマンドをDelphi/400(PC側)から実行して結果を参照する事で、
より便利に活用する方法をご紹介いたします。
 
 
IBM i 側 コマンド発行
プログラム参照表示 (DSPPGMREF) コマンドは、
指定したライブラリ内の特定または全てのプログラムを対象として、
参照しているファイルやプログラムを一括で参照したり、ファイルに出力したりすることが可能です。
※公式ドキュメント:
【プログラム参照表示 (DSPPGMREF) – IBM Documentation】
基本的には以下のようにパラメータを指定してコマンドを発行します。
DSPPGMREF PGM(LIBNAME/*ALL) OUTPUT(*OUTFILE) OBJTYPE(*PGM) OUTFILE(OUTLIB/Z_LIBNAME)
<パラメータの説明>
- PGM:表示される(解析したい)プログラムを指定。
- 修飾子1:プログラム(名称指定・総称*・*ALLが指定可能)
 - 修飾子2:ライブラリー(名称指定・*LIBL・*ALLなどが指定可能)
 - 「*ALL/*ALL」を指定すると処理に非常に時間がかかるため、
ライブラリは単独指定、プログラムは*ALLが推奨。 
 - OUTPUT:出力方法(*, *PRINT, *OUTFILE)
- エミュレータ上では画面表示(*)や印刷(*PRINT)も可能だが、
Delphi/400で結果を参照する場合は「*OUTFILE」を指定 
 - エミュレータ上では画面表示(*)や印刷(*PRINT)も可能だが、
 - OBJTYPE:オブジェクト・タイプ
- *ALLがデフォルトだが、*PGMや*SQLPKGのみ参照も可能
 
 - OUTFILE:出力を受け取るファイル
- OUTPUTを「*OUTFILE」に設定した際に結果を出力するファイル
ライブラリにはQTEMPも指定可能 
 - OUTPUTを「*OUTFILE」に設定した際に結果を出力するファイル
 - OUTMBR:出力メンバー・オプション
- OUTPUTを「*OUTFILE」に設定した際に結果を出力するファイルのメンバー設定
初期値は*FIRST 
 - OUTPUTを「*OUTFILE」に設定した際に結果を出力するファイルのメンバー設定
 
 
 
コマンド結果ファイルの参照
DSPPGMREFコマンドでOUTPUTパラメータを「*OUTFILE」に設定すると、
指定した場所に以下のレイアウトのファイルが出力されます。
この出力ファイルでは参照されるオブジェクトおよびレコード様式単位でレコードが生成されます。
Delphi/400からは、TAS400のRemoteCmdなどでコマンドを発行した上で、
このファイルをTFDQueryなどからSQLでSELECTするといった方法で参照が可能です。
(物理ファイルなので、WHERE句での絞り込みも可能です。)
<出力項目の説明>
※IBM公式サポートページ:
Detailed File Field Description for File QADSPPGM, Format QWHDRPPR for the DSPPGMREF output file
※主にDelphi/400から見るべき重要なフィールドのみ太字
 
- WHLIB :ライブラリー
 - WHPNAM:プログラム
- ヒットしたライブラリ名とプログラム名です。
 
 - WHTEXT:テキスト記述
- ヒットしたプログラム(WHLIB/WHPNAM)のテキスト記述です。
 
 - WHFNUM:参照オブジェクト数
- 参照されるオブジェクトの数です。
ヒットしたプログラムの出力結果においてレコード様式がすべて単一の場合は、レコード数とも一致します。 - 例外として、何も参照しないプログラムでは0が入ります。
 
 - 参照されるオブジェクトの数です。
 - WHDTTM:検索日付時刻
- DSPPGMREFコマンドを発行した日付時刻です。
(世紀+YYMMDD+HHNNSS形式。全レコード同値) 
 - DSPPGMREFコマンドを発行した日付時刻です。
 - WHFNAM:参照オブジェクト
- 参照先のオブジェクト(ファイルやプログラムなど。以下『対象』)の名前です。
 
 - WHLNAM:ライブラリー
- 対象を含むライブラリーです。
コンパイル時の状況によって固定値だったり*LIBLだったりします。 
 - 対象を含むライブラリーです。
 - WHSNAM:ソースファイル名
- 対象がファイルで、メンバーを指定している場合はそのメンバー名が表示されます。
 - 特に指定していない場合はWHFNAMと同値(*FIRSTのメンバー)が表示されます。
 
 - WHRFNO:レコード様式の数
- 対象に含まれるレコード様式の数。
(PRTFの場合は2以上になることがあります) 
 - 対象に含まれるレコード様式の数。
 - WHFUSG:ファイル使用法
- 1=I,2=O,3=I/O,4=U,5=I/U,6=O/U,7=I/O/U,8=N/S,0=N/A
と注釈には記載されていますが、詳細は次の4項目の合計値です。 -  1 = 入力(I)
2 = 出力(O)
4 = 更新(U)
8 = その他の処理(N/S) - 例えば、対象ファイルの読み取りだけ行っている場合は「1」となります。
読み取りと更新を行っている場合は1+4で「5」となります。 - また対象がプログラムでCALLしている場合など、
この4項目のいずれにも該当しない場合は「0」となります。 
 - 1=I,2=O,3=I/O,4=U,5=I/U,6=O/U,7=I/O/U,8=N/S,0=N/A
 - WHRFNM:レコード様式
- 対象ファイルの参照されるレコード様式の名称です。
 
 - WHRFSN:様式レベル識別コード
- 内部値(考慮不要)
 
 - WHRFFN:フィールドの数
- 対象ファイルが持つフィールド数です。ファイルでない場合は0です。
 
 - WHOBJT:オブジェクトタイプ
- F=ファイル P=プログラム D=データエリア 空白=それ以外
 
 - WHOTYP:オブジェクトタイプ
- WHOBJTよりも詳細なオブジェクトタイプが表示されます。
(*FILE・*PGM・*SRVPGMなど) 
 - WHOBJTよりも詳細なオブジェクトタイプが表示されます。
 - WHSYSN:システム名
- DSPPGMREFコマンドを発行したシステム名です。(全レコード同値)
 
 - WHSPKG:参照元オブジェクトタイプ
- PGM=P, SQLPKG=S, SRVPGM=V, MODULE=M, QRYDFN=Q
 
 - WHRFNB:レコード様式の数
- 基本的にはWHRFNOの値と同値
 
 


(CO423/*ALL を対象にコマンドを発行)
 
 
Delphi/400でコマンド発行~結果出力するサンプル
Delphi/400を使ってこのコマンドを発行した場合は、
そのまま以下のようなロジックを記述することでCSV出力が可能です。
CSV出力できれば、Excelで開いてオートフィルタを使用するなど
活用の幅が広がるのではないかと思います。
procedure TForm1.Button1Click(Sender: TObject);
var
  sCmd: String;
begin
  // オブジェクトライブラリの全参照ファイル生成コマンド発行
  sCmd := 'DSPPGMREF PGM(' + Trim(edtLIB.Text) + '/*ALL) OUTPUT(*OUTFILE) ' +
          'OBJTYPE(*PGM) OUTFILE(QTEMP/DHPGM) OUTMBR(*FIRST *REPLACE)';
  AS400.RemoteCmd(sCmd);
  // コマンド発行結果のファイルを参照
  FDQuery1.SQL.Text := 'SELECT * FROM QTEMP/DHPGM';
  FDQuery1.Open;
  // 参照したファイルをCSV出力
  if (not FDQuery1.IsEmpty) then
  begin
    // EXEと同階層に「PGM_ライブラリ名.csv」として保存
    CdsToCsv(FDQuery1, ExtractFilePath(ParamStr(0)) + 'PGM_' + edtLIB.Text + '.csv');
  end;
end;
上のソース内にある手続き「CdsToCsv」は、
TClientDataSetやTFDQueryといったデータセットの内容を
簡単にCSV出力するための共通関数です。以下にそのソースを記載します。
{*******************************************************************************
 目的:データセットの全件CSV出力処理
 引数:Acds:データ出力元のデータセット
       sFileName:出力先フルパス
 戻値:
*******************************************************************************}
procedure CdsToCsv(Acds: TDataSet; sFileName: string);
var
  i: integer;
  sStr: string;
  slOutPut: TStringList;
  // フィールド文字列作成処理
  function FieldString(AField: TField): String;
  begin
    if AField.IsNull then
    begin // フィールドがNULLの場合
      Result := '';
    end
    else if (AField is TNumericField) then
    begin // フィールドが数値タイプの場合
      Result := AField.AsString;
    end
    // ※日付型など数値・文字以外の場合は考慮していないので、必要に応じて追加※
    else
    begin // フィールドが数値タイプでない場合
      Result := AnsiQuotedStr(Trim(AField.AsString), '"');
    end;
  end; //-----------------------------------------------------------------------
begin
  with Acds do
  begin
    try
      // CSV出力処理
      slOutPut := TStringList.Create;
      try
        slOutPut.Clear; // 初期化
        // 先頭行にフィールド名を出力する処理
        sStr := '';
        for i := 0 to FieldCount - 1 do
        begin
          begin
            if sStr <> '' then
            begin
              sStr := sStr + ',' + AnsiQuotedStr(Trim(Fields[i].DisplayLabel), '"');
            end
            else
            begin
              sStr := AnsiQuotedStr(Trim(Fields[i].DisplayLabel), '"');
            end;
          end;
        end;
        if sStr <> '' then
          slOutPut.Add(sStr);
        // 値の出力処理
        First;
        while not Eof do
        begin
          sStr := '';
          // 1レコード分のデータを「,」で作成
          for i := 0 to FieldCount - 1 do
          begin
            if sStr = '' then
              sStr := FieldString(Fields[i])
            else
              sStr := sStr + ',' + FieldString(Fields[i]);
          end;
          slOutPut.Add(sStr);
          Next;
        end;
        // ファイル出力
        slOutPut.SavetoFile(sFileName);
      finally
        slOutPut.Free;
      end;
    except
      Application.MessageBox('CdsToCsvでエラーが発生しました。', 'ERROR', 16);
      Abort;
    end;
  end;
end;