从被调用程序中获取 return 值返回到 rpgle 中的主例程

Getting a return value from a called program back to main routine in rpgle

我正在尝试获得 RPGLE 和 IBM i 方面的经验并不断学习。由于大多数代码似乎都是经典的位置代码,所以我会坚持使用它以适应它。所以我宁愿不使用 /free — /end-free 的东西。顺便说一下,我是在只有 V4R5 的旧 9401-150 上做这个的。

TL;DR:如何从外部调用的 ILE 程序(有自己的 MAIN 部分,也就是说,它本身是独立的)在它自己的激活组(*NEW) 到被叫方?

我已经准备好一个子文件程序,运行 没问题。我想调用一个外部程序来处理子文件中 OPT 值的请求。于是我在callee的D-Specs中定义了一个PR:

DROEDETPG         PR                  EXTPGM('ROEDETPG')
DC_MODE                               LIKE(MODE)
DC_TYP                                LIKE(TYP)         

稍后,我调用程序也能正常工作。

C                   SELECT
C     OPT           WHENEQ    '2'
C                   MOVE      'CHG'         MODE
C                   CALLP     ROEDETPG(MODE:TYP)
C                   ENDSL

这是被调用程序的入口点:

C     *ENTRY        PLIST
C                   PARM                    C_MODE            3
C                   PARM                    C_TYP            16

现在,我想更改的记录可能已经被锁定了。因此,我在外部程序中使用 CHAIN(E),并在 CHAIN 之后从 PF 中获取 %STATUS。它的状态值为 1218,我想将该值返回给调用程序,因此它可能会使用消息行告诉用户,该记录已被锁定且暂时不可用。

我在网上只能找到调用原型并定义可能仅适用于过程的调用接口 (PI)。

所以我想到了一个 "temp file",因为我在 Bash 和 C 在 Unix/Linux 中习惯了这个目的。似乎没有 mktemp() 等效项,但我可以在 QTEMP 中创建具有相同名称的文件。这适用于文件类型 *DTAARA。不幸的是(意料之中?)这个文件只对调用程序可见。也许可以用 SENDERID(*YES) 创建一个全局键控 *DTAQ 但也许这有点矫枉过正?

为什么我不把外部程序中的功能放到一堆函数里用CALLP?好吧,我还在学习。我决定将 Subroutines 中的内容从主要源代码中移出,以结束子例程执行操作时涉及的状态更改的持续麻烦。当 SR 执行 READ 时,数据库指针指向另一条记录,这在继续子文件内容时会带来大量不稳定的行为。

此外,全局变量(字段内容)被覆盖,这会添加更多代码以将内容移到一边,保存数据库的键值,再次调用 SR 并恢复变量,然后执行 SETLL 返回到我以前的状态。我希望这会更容易,但也许我在 ile rpg 方面仍然是一个新手。

我愿意接受有关如何正确避免我的潜在问题(文件指针和全局变量)的其他建议。

因为 ILE 中的参数是通过引用传递的,所以从外部程序获取一个或多个 return 值的一种简单方法是在原型和调用的程序参数中定义它们。来自其他编程环境,这对我来说有点奇怪,因为我被洗脑到 BYVAL 而不是 BYREF 这么多年了,但实际上这就是一个函数 return 值是,堆栈上的一个值被传递回调用者。有些语言会将这些定义为 out 参数,但它们实际上是 inout,您将其视为 out

 DROEDETPG         PR                  EXTPGM('ROEDETPG')
 DC_MODE                               LIKE(MODE)
 DC_TYP                                LIKE(TYP)
 DC_ERR                        7        



 C     *ENTRY        PLIST
 C                   PARM                    C_MODE            3
 C                   PARM                    C_TYP            16
 C                   PARM                    C_ERR             7

此方法的一个优点是,与大多数编程语言中 RPGLE 子过程和函数所做的 return 个值不同,您可以 return 给定任务所需的任意多个值没有定义一个数据结构来保存它们。

QTEMP 是一个很好用的库,但我认为创建一个文件对于通过简单的参数传递可以完成的事情来说有点过分了。不过,我确实认为您可能对 QTEMP 有一些误解。它是在每个作业的基础上定义的,所以如果你在那里写一些东西(在一个文件或一个用户空间对象中,如果你对指针感到满意,这可能比一个文件更方便),它绝对会存在于其他程序中运行 在该作业中查找,直到作业结束或被明确删除。稍微试验一下以确认这一点,如有必要,请单独提出一个问题,因为它确实应该可以用作多个程序的共享存储。

我知道你说不 /free 或 **FREE 但我会忽略它,因为就我而言,固定格式的东西从它产生的地方就可以在地狱之火中燃烧。欢迎您自行将以下代码翻译回固定格式。为了正确地执行此操作,您应该简单地将一个变量传递给将包含错误的程序。

MYPGM.RPGLE

**FREE
/Include MYHDR
Dcl-Pi *N;
    ErrorRet Char(7); // or whatever type you want to return
End-Pi;

ErrorRet = 'RFE1234';
*InLR = *On;
Return;

MYHDR.RPGLE

**FREE
Dcl-Pr MyPgm ExtPgm('MYPGM');
    ErrorRet Char(7); // or whatever type you want to return
End-Pr;

CALLINGPGM.RPGLE

**FREE
/Include MYHDR

Dcl-S Error Char(7) Inz;

MyPgm(Error);
// Error should now contain 'RFE1234'
...

那么,您的第一个问题是 OS 的年龄。如果你在一个更现代的平台上,你可以创建一个带有本地文件描述的子过程,并摆脱这样的文件指针问题:

dcl-proc myFileIsLocked;
  dcl-pi *n Ind;
    mode     Char(3) const;
    type     Char(16) const;
  end-pi;

  dcl-f myfile  keyed usage(*Update);
  dcl-c RECORD_LOCKED      1218;

  chain(e) (type) myfile;
  return (%status = RECORD_LOCKED);
end-proc;

但是,即使在 v4r5 中,您也可以使用子过程来实现您的目标。将您的过程放在服务程序中,并在模块的全局区域中定义该文件。像这样:

对于 V4R5

qrpglesrc,我的模块

h NoMain

fmyfile    up   e           k disk usropn

 /copy qprotosrc,mymodule

pmyFileIsLocked   b                   export
d *n              pi              n
d mode                           3a   const
d type                          16a   const
d*
d RECORD_LOCKED   c                   1218
c*
c                   if        not %open(myfile)
c                   open      myfile
c                   endif
c*
c     type          chain(e)  myfile
c                   if        %status = RECORD_LOCKED
c                   eval      result = *On
c                   endif
c*
c                   close     myfile
c                   return    (%status = RECORD_LOCKED)
p                 e

qprotosrc,我的模块

dmyFileIsLocked   pr              n
d mode                           3a   const
d type                          16a   const

qsrvsrc,我的模块

STRPGMEXP PGMLVL(*CURRENT) SIGNATURE('myModule')
/********************************************************************/
/*  ------ DO NOT CHANGE THE ORDER OF THESE EXPORTS!!! ---------    */
/*  ------ ADD NEW SYMBOLS TO THE END OF THE LIST ONLY ---------    */
/********************************************************************/
  EXPORT SYMBOL(myFileIsLocked)
ENDPGMEXP

然后像这样创建模块和服务程序:

CRTRPGMOD  MODULE(MYMODULE) SRCFILE(QRPGLESRC)
CRTSRVPGM  SRVPGM(MYMODULE) SRCFILE(QSRVSRC) BNDDIR(MYMODULE) +
            STGMDL(*INHERIT)

注意命名。我将我的服务程序命名为与模块相同的名称,以及服务源和绑定目录。我总是为每个服务程序创建一个绑定目录。这避免了稍后更新服务程序时的名称冲突。我还创建了一个公共绑定目录,用于构建使用服务程序的程序。但我离题了。如果服务程序不依赖于任何其他程序,则此服务程序特定绑定目录可能为空。那不是问题。在某些时候,您可能会扩展服务程序,然后您可能非常需要向绑定目录添加一些内容。

全部构建完成后,您可以简单地

c                   if        myFileIsLocked('CHG': 'TYPE')
c*                    do something
c                   endif