您好,欢迎来到客趣旅游网。
搜索
您的当前位置:首页AS400服务程序笔记

AS400服务程序笔记

来源:客趣旅游网
 Service program 一 基本概念: Service program是由module和其他Service program组成的,在系统中用*SRVPGM来表示(好像是废话- -!),其包含了很多可被其他程序调用的procedure。

Service program是用by reference方式绑定,功能类似于其他语言的函数库,提供了很多procedure供其他ILE program调用。

Service program中哪些procedure可以被其他program调用,哪些不可以呢?这就牵扯到了Service program中一个非常重要的概念:public interface。

二 公共接口(public interface)

Service program的public interface规定了哪些procedure和data item可以被外界使用,哪些不可。如果public interface设置的不合理,很容易产生program与Service program不兼容的问题,导致程序发生异常。那么如何定义Service program的public interface呢,如下: CRTSRVPGM命令的EXPROT参数规定了Service program如何对外提供接口,并提供2个参数值: 1 EXPORT (*ALL)。选择*all的话,那么Service program里所有使用key word:EXPORT修饰的procedure和data item都可以被外界程序使用。 2 EXPROT(*SRCFILE)。*SRCFILE是默认的参数值。使用*SRCFILE的话,可以使用binder language(BND文件)来自定义public interface,自己选择哪些procedure和data item可以被外界使用(这些 procedure、data item也必须使用key word:EXPROT修饰)。

注:binder language的文件类型是BND,默认的SOURCE FILE是QSRVSRC,并且BND文件是不需要编译的。

下面说一下*ALL和*SRCFILE各自的优缺点:

*ALL的优点:使用起来非常方便,不需要额外的辅助文件,Service program中所有的export procedure 和data item就都可以被其他程序调用。 *ALL的缺点:兼容性太差,无论增加或者减少Service program的export procedure、data item,都会导致program与Service program的不兼容。那么所有用到此Service program的program都需要重新绑定(UPDPGM)或者重新编译(CRTPGM)才能正常使用。就算是这种变化并不会影响程序的正常运行(比如说你向Service program中追加了一个新的procedure,并且现程序并不使用此procedure,即增加procedure不影响现在的程序运行)那么所有用到此Service program的program也要重新绑定或编译。很麻烦吧。

*SRCFILE的缺点:*SRCFILE使用起来要比*ALL麻烦些,因为要使用binder language定义BND文件才能使用。

*SRCFILE的优点:提供了很好的兼容性。即增加或者减少Service program 的 export 的procedure、data item,程序仍然运行,不需要重新编译相关的program。

那到底是什么原因产生的兼容性问题?听我慢慢道来

三 signature与binder language 1 signature

首先系统为Service program提供了一个类似PF的level check的功能,来检查ILE program与Service program当前的接口是否一致。大概的过程如下:

1)CRTSRVPGM时,系统会根据Service program 的export procedure、data item的数量、名称、顺序产生一个signature(类型于PF的record format ID,具有唯一性),并保存在*SRVPGM中。例如:

DSPSRVPGM SRVPGM(PASS) DETAIL(*SIGNATURE) splay Service Program Information

Display 1 of 1 Service

program . . . . . . . . . . . . : PASS

Library . . . . . . . . . . . . . . . : MYLIB

Owner . . . . . . . . . . . . . . . . . : MYLIB Service program attribute . . . . . . . : RPGLE Detail . . . . . . . . . . . . . . . . . : *SIGNATURE Signatures: 0000000000000000C5F03F1A5845D322 2)CRTPGM时,会将此Service program绑定到*PGM中,同时会把Service program当前的signature值也保存在*PGM中。例如:

DSPPGM PGM(PASSBY2) DETAIL(*SRVPGM)

Display Program Information

Service

Opt Program Library Signature

PASS *LIBL 0000000000000000C5F03F1A5845D322 QRNXIE QSYS D8D9D5E7C9C0404040404040404040 QRNXUTIL QSYS D8D9D5E7E4E3C9D34040404040404040 QLEAWI QSYS 44F70FABA08585397BDF0CF195F82EC1

3)当*PGM被调用的时候,PGM会激活Service program(即把Service program加入到active group中),在激活的过程中会比较*PGM中的signature值与*SRVPGM的signature值是否一致。如果一致,则程序正常运行;如果不一致,则程序出现异常。 大概就这么个过程。

2 与PF LEVEL CHECK(*YES/*NO)类似,你也可以指定是否进行signature的检查 1)*ALL的话,默认LVLCHK(*YES)并且不能更改,会进行检查。那么当Service program的export procedure、data item发生变化时(增加、减少),相应的signature值也发生变化了,如果不重新绑定,重新编译相关的program,那么就导致了program与Service program的signature值不一样,必然导致不兼容。

2)*SRCFILE的话,可以选择LVLCHK *YES还是*NO。选择*NO的话,就不用细说了,因为*NO不会进行signature检查无论你怎么变化procedure、data item都不要紧。因为系统根本不会对program与Service program的signature进行对比,自然不存在兼容不兼容的问题。(使用*NO要谨慎,没有系统的检查,可能会出现一下不可预知的错误)

选择*YES的话,你可以在一个BND文件中,定义多个signature,只要*PGM中保存的signature值与BND文件中任意一个signature值相当,程序就可以正常运行。举个例子吧:

PGM1用到了SRVPGM1

SRVPGM1有2个export procedure:export P1 export P2 BND文件:

STRPGMEXP PGMLVL(*CURRENT) LVLCHK(*YES) EXPORT SYMBOL(‘P1’) EXPORT SYMBOL(’P2’) ENDPGMEXP

假设经过编译后,PGM1可以正常调用SRVPGM1。

现在SRVPGM1增加了一个export procedure,那么为了保持PGM1与SRVPGM1的兼容,我会增加一个signature,如下: BND文件:

STRPGMEXP PGMLVL(*CURRENT) LVLCHK(*YES) EXPORT SYMBOL(‘P1’) EXPORT SYMBOL(’P2’) EXPORT SYMBOL(’P3’) ENDPGMEXP

STRPGMEXP PGMLVL(*PRV) LVLCHK(*YES) EXPORT SYMBOL(‘P1’) EXPORT SYMBOL(’P2’) ENDPGMEXP

这样的话,即使我不重新编译PGM1,PGM1也可以正常调用SRVPGM1(不过不能使用新追加的P3,只能使用*PRV的export block)

注意:signature的值是由export procedure、data item的名称、数量、顺序决定的,且具有唯一性。为了避免不兼容问题的发生,注意以下几点:

1)不要破坏原来export block中export procedure、data item的顺序和数量。 2)新追加export block(即定义多个signature)的话,新的export block必须要包含原来的export block中的内容,并且顺序、数量都不能改变。新追加的export procedure、data item要放export list的最后面。(如上面例子所示)

3)不要删除原export block中的procedure、data item 此外,还可以通过自定义signature的值,也可以解决兼容性问题,这里就不多说了。

四 service program的激活

Service program不能够直接激活。调用SRVPGM的ILE PROGRAM被调用的话,那么此SRVPGM就自动被激活。

Service program的激活过程中,主要干2件事: 1 通过符号链接与物理地址的转换,完成绑定工作。 2 进行signature checking。

这两步之前都进行过详细说明,不再叙述。

—-----—补充说明—————————————————————————

五 service program的用途、目的、优势

为什么要使用service program呢?service program能给我们提供什么好处呢? 我们之所以选择service program,主要从以下2方面进行考虑:

1 采用service program,能使系统维护更加方便,提高可重用性(我认为是最主要的原因)

2 采用service program,可以节省硬盘空间,节省内存空间。 下面还是摆事实、讲道理吧

1) 可维护性:

假如说有N个程序,都会调用MOD1,不使用service program方式的话,就得使用by copy的方式绑定,即:CRTPGM (PGM1) MODULE(MOD1)。那么,如果MOD1发生变更的话,这N个程序都需要重新绑定(UPDPGM)或者编译(CRTPGM),才能使用更新后的MOD1。 如果使用service program的话,把MOD1加入到SRVPGM1中。当MOD1再次发生变更时,如果MOD1的变更不影响SRVPGM1的public interface(即PGM1与SRVPGM1仍然兼容),那么你只需要UPDSRVPGM即可,其他相关联的N个程序都不需要重新绑定或编译;如果MOD1的变更影响到了SRVPGM1的public interface的话(比如说MOD1新增加了一个export procedure,即PGM1与SRVPGM1不兼容),那么你UPDSRVPGM之后,也只需要UPDPGM(或CRTPGM)与新追加的export procedure相关的PGM即可,不需要UPDPGM所有的PGM,方便吧。

2)节省空间: 使用by copy的方式绑定,N个程序中就有N个MOD1的代码。如果使用service program,那么整个硬盘中实际上只有一个MOD1的代码,节省了硬盘空间。

而在实际运行中,所有的用户实际上是共用一份service program的代码,系统会给每个用户分配的静态变量区,但是代码是共用一份的。而使用by copy的方式,每个程序中,还是会包含一分MOD1的代码,这样service program也就节省了内存空间。

六 实际使用情况

如果一个procedure被多个程序调用的话,那么就应该把它加入到service program中。如果这个procedure仅仅是一次性使用的话,那么就没必要了。 我认为理想的情况是,ILE *PGM应该由一个main procedure/module(提供PEP)+ 其它service program组成。

具体的应用情况是,可以把功能类似的程序组成一个service program。比如把各种登录验证的程序,归为一个service program;各种日期转换,日期编辑的程序放到一个service program;还有各种处理代码程序,放到一个service program等。

使用service program主要的一个麻烦需要注意 如何避免 Program signature violation

影响signature的东西主要是Exports,包括名称,顺序等,如果exports任何一个名称或顺序有明变化,则使用到该service program的所有程序都需要重新编译一遍,否则就会发生program signatue vionation.

一个service program主要是由可运行的procedure和data items组成

相应地,在service program里有两类exports, 一类是procedures的 export, 一类是data items的exports,

可以分别用下面两个命令看到。

DSPSRVPGM SRVPGM(SVRPGMNAME) DETAIL(*PROCEXP) DSPSRVPGM SRVPGM(SVRPGMNAME) DETAIL(*DTAEXP)

如果会用bind language的话, 通过指定exports的名称或顺序的话, 在创建service program时 export选项用*srcfile, 可以避免因为service program里新增加prodecure而不得不重新编译所有使用到该service program的programs的问题。

extproc和extpgm的区别

====================================================== 1.调用对象

Extproc:main procedure和subprocedure Extpgm:OPM和ILE *pgm

2.调用性质:

Extproc:静态调用 Extpgm:动态调用 3.传参方式:

Extproc:by reference、by read-only reference、by value Extpgm:by reference、by read-only reference 4.示例:

Extproc Demo: pass1.rpgle

HNOMAIN /INCLUDE QCPYSRC,PASSBY

P BYVALUE B EXPORT D BYVALUE PI 40A D PARM1 2P 0 D PARM2 20A VALUE C IF PARM1 = 1 C RETURN PARM2+'LIKE RPG III' C ELSE C EVAL PARM2 = 'LIKE RPG' C RETURN PARM2+'LIKE RPG IV' C ENDIF P E ====================================================== pass2.rpgle

D AAA PR 40A EXTPROC('BYVALUE') D A1 2P 0 D A2 20A VALUE D NUM S 2P 0 D STRING S 20A INZ('KKK') D RESULT1 S 30A C EVAL NUM = 1 C EVAL RESULT1 = AAA(NUM:STRING) C RESULT1 DSPLY C EVAL *INLR = *ON

C RETURN

Extpgm Demo: DDS CODE:

A..........T.Name++++++RLen++TDpB......Functions+ A R ABCR A F1 10A

====================================================== RPG CODE:

FFilenameIPEAF........L..I........Device+......KExit++Entr FABC O E DISK C *ENTRY PLIST C PARM MLNAME 10 C MOVELMLNAME F1

C WRITEABCR C SETON LR C RETRN ====================================================== RPGLE CODE:

DTESTADD PR EXTPGM('ADD') D 10A D PASSSTYLE PR

D P2 10A D PASSSTYLE PI D P2 10A

D P1 S 10A C C CALLP TESTADD(P2) C EVAL *INLR = *ON C RETURN

关于UPDSRVPGM后,新版本module立即生效的问题 问题: 做过ILE 模式开发的同学都有过这样的经历:

已经执行过的service program,重新编译了一个module后,并且更新了相应的service program (UPDSRVPGM),然后再次调用这个service program下的这个module,但是结果显示module并没有更新,举个例子:

整个程序的组织结构关系,我描述的还清楚吧,现在执行: = =>CALL PGM(PGMA), 则会在屏幕上显示:AAA。

下面把MOD1的功能改成:DSPLY BBB,即:

然后重新编译MOD1、更新SRV0,重新执行CALL PGM(PGMA) 得出的结果仍然是:AAA

刚才明明重新创建了MOD1,并且也进行了UPDSRVPGM,应该打印的是BBB,为什么还是AAA呢?如何才能让程序调用最新的Module呢?

解决办法:

1 最简单粗暴直接的办法就是sign off ,再重新登陆。 2 用命令RCLACTGRP ACTGRP(QILE)

下面是我对此的理解(正确性有待验证):

当更新一个PGM(module)时,旧的程序版本会被系统移到QRPLOBJ这个library中。如果在更新PGM(module)时,PGM(module)恰好处于活动的状态,那么系统会自动让用户继续使用QRPLOBJ中的旧版本程序,而不会报错,当然用户也无法使用新版本的程序。

当再次加载此程序的时候,才会使用新版本的程序。 那什么时候会再次加载程序? 对于service program而言:

1 program运行时系统会检查其activation group是否创建,若没有创建,则会创建activation group。然后系统会在activation group中检查相关的service program是否已经被激活,如果service program尚未被激活,则系统会激活service program;如果service program已经被激活,就不会再次进行激活。简单的说service program 只有在PGM首次运行的时候,才会被激活,并且此后这个service program一直处于活动状态,直到activation group被删除。(RED BOOK)

2 这段完全是我个人的猜想,没找到相关的理论做支撑。当更新service program时,因为service program在第一次被调用的时候,已经被激活了,所以仅仅更新了ASP(硬盘)中的service program,而没有更新activation group中的service program。这时候最直接的想法就是让activation group重新加载service program,这样就可以把ASP中新版本的service program重新加载到activation group中了。但是系统并没有现成的命令,可以删除activation group中的service program再进行重新加载,所以我们要用SIGN OFF和RCLACTGRP来先删除activation group,这样activation group中的所有资源就都被释放掉了。然后PGM A再次运行的时候系统自动又会创建activation group,然后会重新加载service program,这样就会使用到新版本的service program了。 下图为我设想的简易流程图

Data Area和Data Queue的区别分析(一) 网上流传一道题:data area和data queue的区别。 我个人觉得data area和data queue除了长的比较像之外,其可比性并不大。从大的方向说,data area和data queue的共同点仅仅是二者都可以完成不同job间的通讯的功能(即数据交换),但是相同的功能还可以由 PF,user space等完成,为什么没人把它们都拿来一起比较呢?因为长的不像??hoho~~~

下面分别介绍一下Data Area和Data Queue,希望能给困惑者带来一点帮助,不对的地方欢迎指正。

Data Area

一 基本信息

Data Area在系统中用*DTAARA来表示,实际上OS400把Data Area当作一个单独的字段。 Data Area中存储的数据类型有3种,分别是*CHAR、*DEC、*LGL。它们的最大的长度分别为:

*CHAR——2000个字符

*DEC ——1个数字(最长为24 digits, 9 decimal positions。实际上长度不能超过15) *LGL ——1个逻辑值(‘1’或者‘0’)

由此可见,Data Area所能保存的信息量十分有限,对于*DEC和*LGL来说,仅仅能保存一个数值;对与*CHAR类型来说,能保存2000个字符的数据。所以,Data Area不适用于大量数据的处理。通常用Data Area来保存一些被多个job共用的控制信息。另外,Data Area仅仅是一个单独的字段,不需要像PF中那样用游标来定位记录,所以打开一个Data Area的系统开销要比打开一个PF要小的多,效率高得多。这就是Data Area最大的优点。

二 使用方法

Data Area的创建、删除、修改、显示可以用CL命令在命令行上操作,或者在CLP程序中操作,都比较简单,就不多说了。下面说一下在RPGLE中的使用方法。

RPG中关于Data Area的操作符有以下几个:IN、OUT、*LOCK、UNLOCK, IN:读取Data Area

OUT:输出更新Data Area并解锁 *LOCK: 为Data Area加上锁 UNLOCK:为Data Area 解锁

下面举个例子来说明Data Area的基本操作:

D Total DS DTAARA(TotDat) //定义Data Area D Amount 8 S 2OVERLAY(Total:1)

C *LOCK IN Total //读取并且锁住Data Area * // your code……

C OUT Total //更新并且释放Data Area锁 OR

C *LOCK IN Total //读取并且锁住Data Area * // your code……

C UNLOCK Total //释放Data Area锁

关于普通Data Area的锁,做个说明:

1 Data Area的锁是独占只读锁(exclusive, allow read),如果某Data Area已经被锁住了,那么其他的作业只能以只读的方式访问此Data Area,不能用OUT操作。

2 如果想在程序中更新Data Area,则必须先锁住此Data Area(跟UPDATE PF一个道理)。如果没有锁住Data Area,就在那么在程序中用OUT的话,程序会报错,例如: C IN Total

* // your code…… 没有锁住data area就输出,是不可以的。 C OUT Total

3 Data Area上锁可以通过

1)使用*LOCK IN 方式

2)使用Data Area Data Structure方式(稍后介绍)

4 Data Area 解锁可以通过 1)OUT——更新并解锁 2)UNLOCK ——解锁

3)程序结束并且*INLR=*ON,则锁自然解除。

三 Data Area Data Structure

这种类型的Data Area有点特殊,使用起来会方便一些 定义:

D Total UDS DTAARA(TotDat) //定义Data Area D Amount 8 S 2OVERLAY(Total:1)

Data Area Data Structure与普通的Data Area定义有一点差异,就是在DS keyword的前面加上一个U,即:UDS 使用方法:

Data Area Data Structure的特别之处就在于,它隐含了两行的代码,如下: C *LOCK IN Total //读取并且锁住Data Area *……

C OUT Total //更新并且释放Data Area锁

就是说如果定义成Data Area Data Structure的话,那么程序会自动读取并锁住此Data Area,并且在程序结束的时候,会自动输出更新Data Area并释放锁。所以如果定义成Data Area Data Structure的话,就不需要上面的两行代码了。

四 *LDA——Local Data Area

*LDA算是一种特殊的Data Area,它是长度为1024个字节的字符串,并由系统自动为每个job创建和删除。

每个job只能操作自己的*LDA,不能操作、获取其他job的*LDA的数据。就是说在同一个job内,你愿意怎么折腾都行,但是你不能共享的job的*LDA,别的JOB也不能你的JOB的*LDA。不过有一种特例,即递交出去的作业是可以继承原来作业的*LDA的内容。假如JOB A 用SBMJOB递交出了一个JOB B,那么系统会用JOB A的*LDA的内容来初始化JOB B的*LDA。

因为*LDA是私有的,不用担心其他job会锁住LDA,所以LDA的读取和更新不用上锁,也不会报错。

D Local DS DTAARA(*LDA) //定义*LDA D orderNum 5 Overlay(Local:5) *

C IN Local

C EVAL orderNum=’12345’ C OUT Local

如果Data Area是*LDA的话,不用锁住Data Area,更新Data Area也不会报错,不是*LDA的话,以上代码就会报错。

数组与DS用法总结 ARRAY

一 array与table的区别

Array:可以使用下标,进行顺序访问或随即访问

Table:不可以使用下标,只能顺序访问。只能以TABxxx开头。 注:table虽然和array的功能相似,但是array要比table灵活很多。Table可以淘汰了,了解table的原因是维护老的代码时可能会用到。以后就不说table了。

二 array的类型和key word

1有三种类型的array,分别是compile time array、run time array、pre-run time array。

2与array相关的key word:

DIM(number) 定义数组中元素的数量 ASCEND/DESCEND 元素在数组中升序/降序排序

PERRCD(number) 对于compile time array每一行中元素的个数 CTDATA 定义compile time array FROMFILE/TOFILE pre-run time array EXTFMT

举例:

//array1定义10个元素,每个长度为5

D Array1 S 5 0 DIM(10)

//array2是compile time array,共6行,每行3个元素,每个元素10个长度,升序排序 D Array2 S 10 DIM(6) PERRCD(3) CTDATA ASCEND //compile time array要在程序的结尾定义数据 ** CTDATA ARRAY2

Element1 Element2 Element3 Element4 Element5 Element6 Element7 Element8 Element9

Element10

注:如果compile time array的数据发生变化,那么要更改程序源代码中的元素值,并要重新编译程序。

在程序的末尾要有** CTDATA 。建议使用** CTDATA array_name的方式,但是要注意array_name要与D-spec 定义的array_name要一致;仅用**CTDATA(不指定array_name)时要注意** CTDATA的数据按照D-spec定义的array的顺序进行匹配。并且两者不能混着用。

3 Index:index不能为0或负数,不能大于元素个数。

三 array相关的%BIF

1 %Xfoot 数组求和

Sum = %Xfoot(array_name)

2 %LookUpxx 查找(%TLookUpxx for table)

Index = %LookUpXX(argument:array{:satrtindex}) %LookUp 查找第一个=argument元素的index %LookUpLE 查找第一个<=argument的元素index

%LookUpLT 查找第一个的元素的index %LookUpGE 查找第一个>=argument的元素的index %LookUpGT 查找第一个>argument的元素的index

注:除了%LookUp外,使用其他的%LookUpxx的数组必须是排过序的。

3 %Size 返回变量或常量所占的字节数 Size = %Size(name{:*all})

D SmallFiled S 5P 2

D MyArray S 5 DIM(%Size(Smallfield))

SizeSmall = %Size(SmallField) //SizeSmall = 3

ArraySize = %Size(MyArray) //ArraySize = 5 单个元素的size Arraysize = %Size(Myarray:*all) //Arraysize = 150 所有元素的size

4 %Elem 返回数组中元素的个数 Number = %Elem(array_name)

Data Structure

一 DS的key word

OCCURS(number) 定义DS数组

OVERLAY(name:{pos}) 在DS中重定义sub-filed PREFIX(prefix) 为外部的字段增加前缀。

EXTNAME(name:{fmt_name}) 参照的外部文件格式,文件不需要在F-spec定义 EXTFLD(field_name) 将外部数据格式的字段重命名 QUALIFIED 指定引用

LIKEDS 引用DS的格式,隐含自动声明QUALIFIED

LIKEREC 引用外部文件,文件要在F-spec中定义,隐含自动声明QUALIFIED LIKE 参照字段定义

简单的例子:

FItem_PF IF E K DISK

D keystr DS LIKEREC(Item_fmt) *

D #lda E DS EXTNAME(lda)

*

D cust DS QUALIFIED

D name 50a D id 10i 0 *

D part DS QUALIFIED D name 25a D id 10i 0 *

D part1 DS LIKEDS(part) D part2 DS LIKEDS(part)

二 DS的用法

1 group field(字段组合使用)

D Stamp DS

D OrderNo 5 D OrderDate 6 0 D OperInit 3

2 subdivide field(字段拆分使用)

D DS

D ProductNo 16

D model 4 overlay(productNO:1)

D fabric 6 overlay(productno:*next) D color 3 overlay(productno:*next) D size 3 0 overlay(productno:*next)

注:Subfield的默认类型是S和A。还可以定义成P和B

数组和DS配合使用 DS和ARRAY配合使用,属于编程方面的小技巧,使用得当的话,会大大简化程序代码。

1 DS数组: D ARR DS DIM(10) QUALIFIED D SUB1 5A D SUB2 5S 0 2 二维数组:

D ARR DS DIM(10) QUALIFIED D

SUBARR 10 0 DIM(20)相当于定义了一个arr[10][20]的二维数组 使用方法:

C FOR i=1 to %elem(arr) C FOR j=1 to %elem(subarr)

C eval sum += arr(i).subarr(j)

A 3 如果PF有一些连续的相同属性的字段,可以考虑把他们放到array中来处理,比如:

1)连续的字段位于文件的最开始:

R REC

A FLD1 5P 0 A FLD2 5P 0 A FLD3 5P 0 A OTHER 10A

处理代码:

FMYFILE IF E DISK

D myDS E DS EXTNAME(MYFILE)

D fldArray LIKE(FLD1) DIM(3) D OVERLAY(myDs) C READ MYFILE

C FOR i = 1 to %ELEM(fldArray) C* ... process fldArray(i) C ENDFOR

2)连续的字段位于文件的中间: A R REC A OTHER1 10A A FLD1 5P 0 A FLD2 5P 0 A FLD3 5P 0 A OTHER2 10A

FMYFILE IF E DISK

D myDS E DS EXTNAME(MYFILE) D fldArray S LIKE(FLD1) DIM(3) D BASED(pFldArray) D pFldArray S * INZ(%addr(FLD1)) C READ MYFILE

C FOR i = 1 to %ELEM(fldArray) C* ... process fldArray(i) C ENDFOR

这种情况下,先定义一个指针,获取第一个重复字段的地址,相当于获取数组的首地址,也就相当于获取了这个数组。

3)如果PF中的字段有周期性重复的,比如:

A R REC

A FIELD 5P 0 A OTHER1 10A A FLD1 5P 0 A OTHER2 10A A FLD2 5P 0 A OTHER3 10A A FLD3 5P 0

FMYFILE IF E DISK

D myDS E DS EXTNAME(MYFILE) D fldArray DS DIM(3) BASED(pFldArray) QUALIFIED

D sub1 LIKE(OTHER1) OVERLAY(fldArray:1)

D sub2 LIKE(FLD1)

OVERLAY(fldArray:*NEXT)

D pFldArray S * INZ(%addr(FLD1)) C READ MYFILE

C FOR i = 1 to %ELEM(fldArray) C* ... process fldArray(i) C ENDFOR

把重复出现的字段看成一个整体,来作为array的元素,然后再拆分。

ILE模式下绑定方式介绍 一 绑定的基本概念(看图说话)

CRTPGM PGM(PGM1) MODULE(MOD1 MOD2) BNDSRVPGM(SRV0)

By copy :创建PGM1的时候,MOD1,MOD2对象被copy到PGM1中。PGM1内部可以不经过地址转换,直接访问MOD1,MOD2。这种绑定方式就叫做by copy。

By reference :创建PGM1的时候,SRV0并不包含在PGM1中,SRV0和PGM1是两个的对象。但是会在PGM1和SRV0直接建立一个符号链接,把他们联系起来。当PGM1使用到SRV0的时候,编译器会把这个符号链接转换成SRV0的物理地址,这样PGM1就可以访问SRV0了。这种绑定方式叫做by reference。

二 绑定的过程。 By copy

编译期:CRTPGM的时候,PGM1和MOD1之间的物理绑定。二者成为了一个整体,完成绑定。

By reference

编译期:PGM1与SRV0之间建立符号链接。

运行期: 当PGM1被调用的时候,系统会根据PGM1和SRV0之间的符号链接,转换成SRV0的物理地址,通过SRV0的物理地址找到SRV0并把SRV0加在到activation group中,完成绑定。

注意:by reference方式在运行期加载service program是发生在PGM1被调用的时候。而不是PGM1调用SRV0的时候。当PGM1调用SRV0的时候,SRV0已经完成了绑定。从这个角度来说,by reference的执行速度与by copy是一样的。

总的来说,bound by reference的运行效率介于动态调用和bound by copy之间。

三 绑定的规则

使用by copy的情况有3种,如下:

1. MODULE parameter上指定的module,都是以by copy的方式绑定。这个没什么好说的。 例如:CRTPGM PGM(PGM1) MODULE(MOD1 MOD2) BNDSRVPGM(SRV0)

2.Binding Directory 里的module,并且这个module为unresolved import提供与之匹配的export。 例如:Binding Directory :BD1 有如下 4个entry

Object Type Library M11 *MODULE *LIBL M22 *MODULE *LIBL S1 *SRVPGM *LIBL S2 *SRVPGM *LIBL PGM2:

H BNDDIR(‘BD1’) ……

C CALLB ‘M11’ ……

此时,PGM2需要引用(unresolved import)M11,而BNDDIR刚好提供了M11(export),所以M11也以by copy的方式绑定到PGM2中。虽然BNNDIR中还包含一个M22,但是因为PGM2不需要M22,所以M22被忽视,不会被绑定到PGM2中。

3.Binding Directory里的module,虽然这个module并不为unresolved import提供export(比如下面的M3),但是这个module的export却出现在BND文件的*current block中。那么这个module也是作为by copy的方式绑定的。说的比较绕嘴,难理解,举个例子吧: 例如:Binding Directory :BD2 有如下 2个entry

Object Type Library M2 *MODULE *LIBL M3 *MODULE *LIBL

Module M1: imports P2 Module M2: exports P2 Module M3: exports P3

Binder language S1: STRPGMEXP PGMLVL(*CURRENT)

EXPORT P3 ENDPGMEXP

CRTSRVPGM SRVPGM(SRV1) MODULE(M1) SRCFILE(S1) SRCMBR(S1) BNDDIR(BD2) 首先M1作为MODULE的参数,肯定是by copy方式绑定的(根据绑定规则1)。M1有一个unresolved import:P2,而binding directory:BD2里的M2刚好提供export P2。所以M2也是以by copy的方式绑定的(根据绑定规则2).而M3,不属于以上任何一种情况,但是M3的export P3,却是BND文

件里当前(*current)的输出接口,是创建service program必须要使用的,所以虽然M3不属于前两个规则的任何一个,但是也要以by copy的方式绑定。

规则3简单点说,创建service program的时候,BND文件(binder language)里需要的exprot ,此export所属的module总是以by copy的方式绑定。

By reference有2种方式,如下:

1. BNDSRVPGM parameter上指定的service program,都是以by reference的方式绑定。这个也没什么好说的。

例如:CRTPGM PGM(PGM1) MODULE(MOD1 MOD2) BNDSRVPGM(SRV0)

2.Binding Directory 里的service program,并且这个service program里的module为unresolved import提供与之匹配的export。这个也不多说了。

call callb callp区别

对于刚刚接触ILE模式开发的初级菜鸟而言,想要搞清楚这三者的区别还是有点难度的。网上虽然一些帖子对这三者进行了比较,但是这些帖子或是语焉不详,或是高度概括。对于老鸟来说或许已经足矣,但是对于初级菜鸟而言,还是理解起来并不容易。下面就由我这个中级菜鸟结合red book和自己的理解,从一个菜鸟的视角,来尽量的把这三者的区别描述清楚。 前提:你已对这三者有些初步的了解。 一 基本概念

1 CALL是用动态方式调用*PGM类型的OPM或者ILE 程序。(即可执行的程序,区别于*MODULE)。 2 CALLB是用静态方式调用module中的main procedure(PEP)。(其实CALLB也可以调用subprocedure,但是不推荐这么用,好像也没人这么用。因为既然已经用定义原型的方式定义了subprocedure,为什么不使用CALLP呢?CALLP比CALLB有更多优势)。

所以CALLB一般用来调用没有事先用原型定义声明的main procedure。(因为main procedure不需要定义原型也能用,subprocedure必须定义原型才能使用。如果mian procedure也定义原型了,那么也用CALLP吧)

3 CALLP 是原型调用方式。顾名思义,使用CALLP必须要定义原型(prototyped),前两者则不用定义原型 。CALLP既可以像CALL那样用动态方式调用OPM或者ILE程序,也可以像CALLB那样静态的调用procedure。CALLP有很多优势,其中很重要的一点是他会在编译期提供参数检查(parameter checking),这样会避免一些运行期(run-time)的错误。

二 调用范围:

CALL:OPM 或者ILE program。(前提:调用的PGM要在当前LIB LIST中存在) CALLB/CALLP: (前提:调用的procedure要在bind directory中有定义)

1同一个ILE program中的procedure。

2同一个ILEprogram中,相同的service program中的procedure 3同一个ILE program中,不同的service program中的procedure。

三 如何选择三者?

CALL和CALLB能实现的功能,CALLP都可以实现,并且CALLP还提供了参数检查,by value ,by read-only reference的传参方式等优势,并且定义个原型也并不是很困难的事,所以尽量使用CALLP。

四 效率

这个地球人都知道,静态调用肯定比动态调用效率高,但是高多少,我没测试过,不清楚。

补充:什么是动态调用,静态调用,编译期参数检查? 1 假设:PGMA调用PGMB

动态调用:PGMA不知道PGMB在系统中的哪个位置,PGMA在调用PGMB的时候(run-time),按照当前的library list依次来寻找PGMB,这就是动态调用。

2 假设:PGM1 中的PROCA 调用PROCB

静态调用:PGM1 在编译的时候,编译器就把PROCB的地址信息保存在PGM1中了(具体binding的方式有by copy 和by reference两种)。当PROCA调用PROCB的时候,PROCA根据PROCB的地址,能够直接找到PROCB的位置,而不用像动态调用那样,要按照library list挨个library来搜索。这种方式就是静态调用。

3 编译期参数检查

Compile-time parameter checking是指在编译的时候,编译器会检查你传给procedure的参数是否和prototype里定义的一样。包括参数类型,以及是否有可忽略的参数等。不一样的话编译会报错,无法通过。能够减少运行期的错误。

假设PGMA 调用PGMB,PGMB 有2个入口参数,如下所示: *ENTRY PLIST

PARM P1 5P 0 PARM P2 5A

如果不用原型定义的话,那么PGMA调用PGMB的时候,传给PGMB2个参数,分别是10P 0和6A,即

CALL PGMB

PARM A1 10P 0 PARM A2 6A

这样的话,PGMA是可以通过编译的,但是实际调用的时候,有可能造成数据异常,或者程序异常。 如果使用原型定义的话:

D pgmbb PR EXTPGM(‘PGMB’) D 5P 0 D 5A

那么当PGMA再次传给PGMB 10P 0 和 10A的参数时,编译就会报错,无法通过, 减少运行期的错误。

CALLP pgmbb(A1,A2) /*编译无法通过

RPGLE传参那些事儿

众所周知RPGLE有3种传参方式,分别是by reference, by value, by read-only reference 下面就这三种传参方式,结合red book来谈谈自己的理解。 一By reference

RPGLE默认的传参方式就是by reference。用CALL/PLIST来调用程序就是典型的by reference方式。 By reference原理:

By reference方式实际上是把变量的指针传给被调用的程序,被调用的程序可以修改变量指针所指向的值,然后把变量的指针返回给调用者,这样调用者就可以使用被修改过的变量了。 By reference有两点注意事项:

1.调用*PGM类型的程序,只能使用By reference(by read-only reference)。

2.调用external program call (EXTPGM)也只能用By reference(by read-only reference)。

二By value

对于使用原型定义的procedure(prototyped procedure),除了使用by reference外,还可以使用by value的方式传参。

By value 顾名思义,是把变量值传递给被调用者,注意仅仅是变量所代表的值,而非变量本身。变量本身不发生任何变化。

实际上,用by value方式,被调用者也可以改变变量值,但是这个改变仅仅发生在被调用的程序范围内。即这种变化对于调用者来说,是不可见的,是透明的。

By value实现原理: Red book并没有给出by value方式的实现原理(我没找到,anyone knows?),但是我根据by read-only reference方式实现的原理,猜测by value的原理,大概应该如下:

当用by value传参数时,编译器会把这个参数拷贝到一个临时变量中,然后把这个临时变量的指针传递给被调用的程序。这样的话,无论被调用的程序对此参数做如何操作,都不会影响原来的参数值,因为他改变的仅仅是临时变量的值。所以用by value方式传参,不会改变原变量的值。

By value的定义方法(key word:value): D function1 PR

D parm1 10a value

By value的优势有哪些呢?

1.它可以使用字符、数字常量和表达式作为参数传递。

2.它可以弱化数据类型的检查匹配。比如你定义个参数 5P 0,你传给它3S 1也是可以的。

3.你也可以像by reference那样用变量作为参数传递,但是从调用者的角度来看,这个变量值是不会被改变的(参见by value原理)

三 by read-only reference

第三种方式比较有意思,因为他是by reference和by value的合体,兼有两种方式的优点。 首先,by read-only reference从字面上来看,它是也是by reference方式,所以适用by reference的地方,也都适用by read-only reference。只不过它前面加了一个限定词read-only,即只读,却不能改变,那么它又具备了by value的特点,这样by value的各种优势他也具备了。

By read-only reference的定义方法(key word:const): D function2 PR

D parm2 10a const

By read-only reference的注意事项:

用by read-only reference传参,要确保这个参数值在被调用的程序中不会被修改,即这个参数是input parameter(有点类似于F-spec定义文件的打开方式,by read-only reference必须用IF的方式打开,不能用UF的方式打开,即read-only parameter不能被修改)。如果被调用的程序会修改input parameter的值,那么放心好了,程序也不会出现任何问题。因为编译就没法通过……

By read-only reference的优点: 参见by value

By read-only reference的原理: 参见by value 四 如何合理的选择各种参数传递的方式 以上三种传参方式各有各的好处。有的情况下3种方式都可以使用,有的情况下只能使用特定的某种方式。从下表可以看出:(文字有点啰嗦,看图一目了然) 1.调用program只能用by reference的方式。(CALL /CALLP EXTPGM) 2调用没有定义原型的procedure,也只能用by reference的方式。(CALLB) 3.by value只能用于调用procedure。(CALLP EXTPROC/EXPRESSION) 4 by reference 适用于所有的CALL 方式。 5有定义原型的procedure,三种传参方式都适用。 CALL CALLB CALLP CALLP EXPRESSION EXTPGM EXPPROC By O O O O O reference By value O O 至于by read-only reference的使用,如果必须用by reference的方式传参,并且你能保证被调用的程序不会更改这个参数值,那么就可以使用by read-only reference了。 关于by value 和by read-only reference应该选那一个,按照red book的说法: 1.如果参数是数字型或者指针型的,那么使用by value在效率上会稍稍高一点。 2.其他的情况,请选择by read-only reference。 论INFDS中的光标如何定位 RPG CODE:

IINFDS DS I B 370 3710WKCRSR C WKCRSR DIV 256 ROW 40 C MVR COL 40

大家对这段代码肯定不陌生吧,是获取屏幕光标的位置。今天想要讨论的不是怎么样使用这段代码,而是为什么这样计算就可以获取光标的位置?

首先information data structure 的370—371位存储的是2 bytes 长度,用16进制表示的坐标值。370 是行数,371是列数。

我曾经想,IBM为什么不用10进制来表示行和列?10进制直观,不用转换,可以直接取过来用,例如:

IINFDS DS I P 370 3700 ROW I P 371 3710 COL

但是后来发现,这里面涉及每个数据类型占用的字节数和取值范围问题。比如以上定义,P表示的是压缩10进制,即每一个数字占用1个byte,那么ROW和COL分别只有1byte的长度,所能表示的取值范围就是(9,9),远远于*DS3(24,80)和*DS34(27,132)这两种屏幕类型的取值范围,所以,P类型不可取。而S是区位10进制,每个数字占2个byte,更不可取了。而对于2进制说,1byte=8bit,

所表示的范围是28=256。所以,把370—371的16进制转换成2进制的话,可以表示的范围就是(256,256),足够表示光标的范围了。

但是还有一点,在RPG中,B类型的字段只能占2个byte,或者4byte,所以不能这样定义: IINFDS DS I B 370 3700 ROW I B 371 3710 COL

虽然这样定义是最简单的,但是系统会提示每个B类型的变量的长度错误。所以只能从整体上把370—371这2 byte长度的16进制转成2进制。也就引出了下面的问题:为什么要除以256才能得到行和列的值呢?

是因为在2 byte长度的16进制转换成2进制的过程中,行数(370)相当于扩大了256倍,举个例子说明下: IAA DS

I P 1 6 0 YYYYMM 如果想要取得年份和月份的话,用数学的方法,要怎样取?是不是这样: C YYYYMM DIV 100 YYYY C MVR MM

这里YYYY相当于扩大了100倍,所以,要除以100。同理可得,16进制的字段扩大了256倍,要除以256。.如果RPG中可以定义B类型为1byte长度的话,那么就可以直接分割INFDS了,就不用这么麻烦了。

讨论到这里,我想你已经明白了,在RPG中如何用information data structure 获取光标值了。下面,再讨论下RPGLE中通过information data structure获取光标的方法,要比RPG简单、省事的多,当然,这多了RPGLE新曾的几种数据类型。 RPGLE CODE:

D INFDS1 DS D ROW 370 370I 0 D COL 371 371I 0 或者

D INFDS1 DS D ROW 370 370U 0 D COL 371 371U 0

这样就获取了光标值了,不用做DIV & MVR了,这又是什么原因呢? 先来看看I类型和U类型的说明: Integer Format

The integer format is similar to the binary format with two exceptions: The integer format allows the full range of binary values

The number of decimal positions for an integer field is always zero.

The length of an integer field is defined in terms of number of digits; it can be 3, 5, 10, or 20 digits long. A 3-digit field takes up 1 byte of storage; a 5-digit field takes up 2 bytes of storage; a 10-digit field takes up 4 bytes; a 20-digit field takes up 8 bytes. The range of values allowed for an integer field depends on its length. Field length

Range of Allowed Values

3-digit integer 5-digit integer Unsigned Format

-128 to 127 -32768 to 32767

10-digit integer -21474838 to 21474837

The unsigned integer format is like the integer format except that the range of values does not include negative numbers.

The length of an unsigned field is defined in terms of number of digits; it can be 3, 5, 10, or 20 digits long. A 3-digit field takes up 1 byte of storage; a 5-digit field takes up 2 bytes of storage; a 10-digit field takes up 4 bytes; a 20-digit field takes up 8 bytes. The range of values allowed for an unsigned field depends on its length. Field length

Range of Allowed Values 0 to 255 0 to 65535

0 to 4294967295

3-digit unsigned 5-digit unsigned

I是整形,U是无符号整形,它们都有一个共通的特点,A 3-digit field takes up 1 byte of storage。3个数字才占用1 byte。1byte的I和U类型表示的范围分别是(-128—127)和(0—255) 所以可以用I和U类型的变量直接分割INFDS,而不用担心范围取值范围过小。 这就是RPGLE如此简单就可以获取光标值的原因了。

PS:1byte长度的I类型的范围是-128—127,对于*DS3(24,80)是够用的,但是对于*DS34(27,132)就不够用了。

CA与CF的区别 2011-07-20 10:22:58

CA不会把屏幕上(DSPF)输入的各字段的值传到RPG中,即在RPG中,DSPF的各字段的值都用‘ ’ 、0 或者*OFF表示。即所谓的CA不传值。

CF则会把DSPF中输入的各字段的值传回到PRG中,RPG可以使用用户输入的实际值。即所谓的CF传值。 例:DSPF: Code:

AAN01N02N03T.Name++++++RLen++TDpBLinPosFunctions+++++++++ ********** Beginning of data **************************** A DSPSIZ(24 80 *DS3) A CA03(03 'EXIT') A* CF03(03 'EXIT') A R FMT A 6 10'ID:' A ID 8A I 6 17 A 8 10'NAME:' A NAME 10A O 8 19 A 10 10'F3 - EXIT'

10-digit unsigned

显示如下: RPG:

.....FFilenameIPEAF........L..I........Device+......KExit++En *************** Beginning of data *************************** FCFCA CF E WORKSTN C EXFMTFMT C MOVELID NAME C SETON LR C RETRN

如果在DSPF中定义CF03的话,那么RPG中,ID =’ANDY’ ,NAME=’ANDY’

如果在DSPF中定义CA03的话,那么RPG中,ID= ‘ ‘ , NAME =’ ‘

PR,PI概念

PI是Procedure Interface; PR是Procedure Reference。 两者是配对使用。PI只能有一个(如果一个PI在一个项目中出现多个,概念已经错误或混乱了。);PR可以有多个,即至少有一个外部的PR要配对内部的、只有一个的PI。

注意:在PI的原型(prototype)第一次编译时,最好把配对的PR定义的原型放在procedure一起进行编译,而不要通过copybook放在一起。因为,PR第一次配对PI进行编译时,如果PR有extproc键字,或者EXPGM键字,会造成第一次配对编译时,OS搜索bnddir或libl,会报外部定义的PR不存在MSG,编译通不过。copybook通常存放大量含义ext键字的PR。

一个procedure 的处理过程,只能通过Pi 接口与procedure外部Pr的参照(Reference)接口对接。想想就可以知道,如果一个项目中同名的Pi有多个,如果同名的Pi处理过程不一致的时候,或代码重复,即使prototype原型都是一样的,OS系统要用哪一个Pi下的处理过程呢? 顺便说一下,目前实施的中信银行核心改造项目早期就出现这样的概念混乱的情况。

如果Pr定义extproc,通过bnddir,采用静态对接一个Pi的Interface地址指针(Pointer),OS允许Pi之外有多个Pr地址指针进行静态对接。OS从Pr发起处理起点通过地址指针对接Pi地址指针,再引用Pi下的处理代码进行过程处理;再原路返回处理结果

服务程序(SRVPGM)创建 CRVPGM: module方式创建,假设要创建STEST服务程序 一个服务程序(SRVPGM)创建命令:

===> CRTSRVPGM SRVPGM(STEST) MODULE(MYLIB/STEST MYLIB/STEST1 MYLIB/STEST2) SRCFILE(MYLIB/QBNDSRC)

STEST服务程序下包括STEST1、STEST2服务应用程序(service): 1) STEST.rpgle服务程序代码: /free

Retrun; /end-free

2) STEST1.rpgle代码:

H nomain h option(*showcpy)

* /include mylib/qcpysrc,STESTPR

* P stest1 B export d stest1 PI 10a d ymd 8p 0 const

* d retval s 10a d stest1 s 10a

* /free monitor; retval = %char( %date(ymd:*iso) : *USA ); on-error;

retval = 'ERROR'; endmon; *inlr = *on; return retval ; /end-free p

stest1 e

3) STEST2.rpgle代码:

H nomain h option(*showcpy)

*

/include pingy/qcpysrc,STESTPR

*

P stest2 B export d stest2 PI 100a d s@string 100a const

*

d stest2 s 100a d retval s 100a *

/free returnval = %trim(s@string) + ' '+ 'Hello, Body!';

*inlr = *on; return retval; /end-free p stest2 e

4) COPY book:/include mylib/qcpysrc,STESTPR

d Stest1 PR 10A extproc('STEST1')

d c@date 8p 0 const *

d Stest2 PR 100A extproc('STEST2')

d c@string 100a const

5) SRCFILE(MYLIB/QBNDSRC)下应包括STEST.BND文件,其内容: STRPGMEXP SIGNATURE('STEST') EXPORT SYMBOL('STEST1') EXPORT SYMBOL('STEST2')

ENDPGMEXP

以上三段程序代码需在用CRTSRVPGM之前,创建成*module代码形式。

创建后的STEST,*SRVPGM服务程序,用一个binddir,STEST挂起。 1) 创建一个binddir: CRTBNDDIR

2) 用wrkbnddir把STEST挂在STEST,binddir下。

做完上述步骤,就可以在其它rpgle程序中引用,如mypgm.rpgle,代码: H BNDDIR(‘STEST’) *

d Stest2 PR 100A extproc('STEST2')

d c@string 100a const *这里也可以统一用/copy 文件定义。建议用/copy 文件。 D String s 100a d c@String s 100a /free …..

String2 = STEST2(c@String1); ….. 补充说明1):如果一个service定义PI:如STEST2.rpgle

P stest2 B export d stest2 PI 100a ….

d stest2 s 100a d retval s 100a ……

return retval;

这个procedure,的输出是可以export,在PI输出的100A,在d表中定义 D stest2 s 100a 用return retval;来体现。

如果没有定义export和PI后的输出属性,且仅return;,返回值只能用PI/PR中的原型接口(prototype)中。PI/PR中的定义必须一致。这时在mypgm.rpgle中使用STEST2,service,只能 Callp STEST2(c@String);

补充说明2):

如果STEST11.rpgle和STEST2.reple对F表定义,且操作,必须去掉H表中的nomain。没有人为定义main,或nomain,默认值是main。

也可以在STEST.rpgle服务程序中定义F表,对其操作。STEST11.rpgle和STEST2.reple的H表中定义nomain,相当于这STEST1和STEST2两个services都是subprocedures,也就是相当于我们熟悉的subroutinee,即begsr/endsr中的代码。

补充说明3):

如果把STEST1.rpgle和STEST2.rpgle生成modules后再用crtpgm生成PGM在SRVPGM中引用,这时在CRTSRVPG中BNDSRVPGM选项定义STEST1.pgm和STEST2.pgm。

补充说明4):

如果STEST1.rpgle和STEST2.rpgle不是用PI定义原型接口(prototype),而是用传统的Plist定义的接口,这时引用程序mypgm.rpgle的PR部分需把extproc键字改为extpgm定义引用的程序名。PR部分按照plist的parm个数和属性来定义。

补充说明5): 如果STEST1(subprocedure)和STEST2(subprocedure)都是在STEST.rpgle代码中,然后生成module或pgm,STEST.rpgle在MYLIB/QSRVSRC目录下在CRTSRVPGM中则需: Service program . . . . . . . . > STEST

Library . . . . . . . . . . . *CURLIB Module . . . . . . . . . . . . . > STEST

Library . . . . . . . . . . . > MYLIB

+ for more values

*LIBL Export . . . . . . . . . . . . . > *ALL Export source file . . . . . . . QSRVSRC

Library . . . . . . . . . . . *LIBL Export source member . . . . . . *SRVPGM Text 'description' . . . . . . . *BLANK 或,

Bind service program . . . . . . STEST

Library . . . . . . . . . . . MYLIB

生成后的STEST,*SRVPG与使用bnd文件生成的STEST,*SRVPG差别: 1) 后者可以用updsrvpgm对STEST服务程序中的STEST1,*module,或STEST2,*module单独进行更新操作。前者,则不能;

2) 前者无需bnd文件,后者必须有bnd文件。

3) 前者,STEST,*SRVPG中的STEST1和STEST2,subprocedures,循序是STEST.rpgle中的代码自然循序。后者,是STEST.BND中的export项的定义循序。

补充说明6):service可以是一个SRVPGM,*pgm;或一个SRVPGM下的一个subprocedure,*module。

服务程序最有效的实际应用案例

对当今中国400应用项目实际应用服务程序,应结合中国400应用项目的实际水平情况具体部署。

中国目前的400应用项目,程序之间的调用关系绝大部分是基于动态调用;程序接口绝大部分是PLIST参数定义的接口。我们可以把服务程序归结为1)无数据文件的功能模块类;2)有数据文件的功能模块类。

这里重点讲有数据文件定义使用的服务程序的结构和调用。 在银行核心系统项目中,最基本的操作之一就是对历年的客户账号的有效性检查。对这类只读性操作,最有效的实施方法就是定义一个服务程序。最简单实用的服务应用程序(service)创建,就是把有效的、实用的程序(PGM),如果只读,去掉seton *inlr;把程序编译成*module,不改动任何程序定义和入口条件,用BIND文件定义成一个服务程序(SRVPGM)下的一个服务应用程序(service)。

把使用同类数据源的服务应用程序用上述方法编译成*module,挂在一个服务程序下,就可以动/静态直接调用使用。

如果对服务应用程序(services)修改,更新过的服务应用程序编译成新的*module,然后用UPDSRVPGM命令直接对修改过的modules直接进行更新,就可以使用了。

指针 指针纵横

一、概念

1.什么是指针?

指针是数据在内存或磁盘上的物理地址。因为OS400是统一寻址指令系统,即物理内存与磁盘是统一寻址,所以,指针没有区分内存指针,还是磁盘指针,都是统一的指针。

2.指针的表示

在OS400下,指针(pointer)是一个16byte,16字节的地址符号,是OS自动分配指针和内容。

3.指针在计算机语言中的应用范围

指针用在除CL之外的高级开发语言中,如C/C++、RPGLE等。

4.如何查看指针地址带出的数据?

在RPGLE debug方式下,在命令行对命名的指针,如Ptr,键入: Eval Ptr :C 100

C表示是字符型方式显示指针带出的数据;X表示16进制显示指针地址带出的数据。 100表示显示数据的长度。

顺便说一下,os400下,debug用F11显示程序变量的长度,默认值是1024字节。如果需要查看1024之后的数据,用eval方式,比如字符变量C_Var;eval C_Var : C 8096,在debug方式下,os400就开辟一个8096字节区域存放变量C_Var的显示内容。

5.如何确定指针是否有效?

在debug方式下,用eval 或F11显示命名的指针,比如Ptr,如果显示为:

PTR = SPP:FC27C1E4F206E1B0,说明这个指针是有效的,否则,OS400会报无效指针信息。

二、指针在RPGLE中的应用 1.指针在RPGLE程序中的定义

指针在rpgle d表中的定义用符号‘*’进行定义。

在RPGLE中,指针是通过D表进行定义,被指针定义的对象范围较广,通常在RPGLE中用指针定义指向一个数据结构,DS;或字符型变量。比如:

D D_MyDs ds likeds(RefDs) D based(Ptr) *

D Ptr s * 或者

D C_String s 1024 varying based(Ptr_2) D Ptr_2 s *

值得注意的是,在D表用based到指针的数据结构或变量,不能用INZ键字,即不能用程序变量初始赋值键字。

2.指针的赋值

在rpgle中,指针的赋值可以直接指针间的直接赋值,比如 Ptr = Ptr_2 ;

也可以通过rpgle的专门赋值building function %addr进行赋值比如:

d C_Var2 s 10 inz('ABC')

d Ptr s * *

/free Ptr = %addr(C_Var2) ;

这时指针Ptr指向数据位字符长度为10,内容为‚ABC‛的数据。 3.指针的应用范围

在rpgle中,通常都是把指针用在程序间传递参数上。

早期的rpg程序基本上都是通过plist对每一个具体参数进行定义。这样的定义对项目联调、修改和代码最终定版,都造成非常大的难度,增加项目的实际开发难度。

我们的成功经验:

在程序间每一个程序代码都定义统一的参数格式,比如 C *entry plist

C parm Ptr_1 C Parm Ptr_2 C Ptrm Ptr_3 这段rpgle代码放在统一的copybook中,在每一程序代码中只要定义: c/copy QTXTSRC,CMS_CD_PLT

其中,Ptr_1只能用在应用项目的系统变量范围,比如交易日,时间等;

Ptr_2只能用在应用项目的输出参数范围,比如PGMA调用PGMB时,PGMA对PGMB传递的参数只能用PTR_2带出;Ptr_3只能用在返回结果参数范围,即PGMB对PGMA的返回值。 对三个Ptr指针引入的参数结构,都可以放在统一的copybook中,在程序代码中直接进行copy定义。这样做的好处,一个应用项目的数据结构是唯一的。

4.指针在QAPI中的应用

指针在QUSPTRUS,QAPI中的编程,是把调用的QAPI引入的信息放在一个数据空间中,再通过专门的QAPI把这些数据用过指针引用。

5.用指针编偏移取数据

通常情况下,最简单的指针应用就是一个指针直接指向数据,比如:

D D_inDs ds likeds(D_Mark1Ds)

D D_outDs ds likeds(D_Mark2Ds) based(Ptr_3) 当程序代码进行调用后, /free

….

D_inDs.C_fld = ‘ABC’ ; ….

//把数据结构D_Mark1Ds的地址赋值给指针Ptr_2。 Ptr_2 = %addr(D_Mark1Ds) ;

Callp CMSFTZ03FR(ptr_1:ptr_2:ptr_3) ;

调用CMSFTZ03FR之后,Ptr_3就直接把返回参数引入到程序数据结构D_outDs中了,直接引用。 如果程序的返回结果是包括一个以上的数据结构,比如通过Ptr_3返回的参数如下: D D_outDs ds based(Ptr_3)

D D_DataDs1 likeds(RefDataDs1) D D_DataDs2 likeds(RefDataDs2) 这时,在程序中就要这样定义和用指针偏移读取数据结构的数据:

D D_DataDsA ds likeds(RefDataDs1) based(Ptr_A) D D_DataDsB ds likeds(RefDataDs2) based(Ptr_B) D Ptr_A s * D Ptr_B s *

/free ….

Callp CMSFTZ03FR(ptr_1:ptr_2:ptr_3) ;

Ptr_A = Ptr_3 ; // 返回数据结构的数据D_DataDs1已经放入D_DataDsA中了。

Ptr_3 = Ptr_3 + %len(D_DataDsA); //把指针Ptr_3偏移到指向第二个数据 结构的起始地址。

Ptr_B = Ptr_3 ; //返回数据结构的数据D_DataDs2已经放入D_DataDsB中了

三、深度探讨指针用法 1.指针层次的概念和用法

在RPGLE中指针是可以嵌套的,是有层次概念的,比如

d D_Ds1 ds based(P_Ptr1)

d D_ParmDs1 likeds(RefDs1)

d P_PtrA * *

d D_Ds2 ds likeds(RefDs2) *

d Ptr1 s * d PtrA s * /free

……

P_PtrA = %addr(D_Ds2) Callp Proc(P_Ptr1) ;

这时,在代码中表示的指针层次是两层。在V5R4下,指针的层次最多可以达15层。

2.多层指针结构下,注意事项

在多层指针结构下,要注意如下事项:

1)指针引入的数据结构DS,最好立即用新的数据结构Ds进行转移保护,如 d D_DsIn ds based(Ptr3) d P_PtrA *

d D_DsParmDs ds likdds(RefDs) based(P_PtrA) d D_DsInBak ds likdds(RefDs) *

d P_Ptr3 s * d P_PtrA s * /free

…..

Callp Proc(Ptr1tr2tr3) ; //判断Ptr3数据结构中的返回码 ……

//如果返回码没有错误,立即备份返回数据结构 D_DsInBak = D_DsParmDs ;

2)在指针引入复杂结构中,实数据放在前面,变长数据结构放在复杂结构后部,如:

d D_InDs ds based(PtrA) qualified d c_String 10

d s_Digit 5 0

d D_ArrDsSet likeds(D_ArrDs) d P_PtrA * d P_PtrB * *

d D_ArrDs ds qualified

d s_count 3s 0 dim(2) d a_ymlst 10a dim(500) d a_mmlst 10a dim(500) *

d D_DsA ds based(P_PtrA) d c_fld 30

d p_fld 12 5 *

d D_DsB ds based(P_PtrB) d s_Atrr 3 0 dim(20) d c_String 1024 varying

3.指针的迁移

在程序之间用指针进行传参调用,因为指针仅仅把参数集的起始指针进行传递。很多情况下,因为程序运行时,OS400在ASP中给每一个程序都分配一个程序运行临时区,当程序获取下一级程序返回的临时区域的变量(参数)指针地址时,本身的程序运行临时区,OS400就会自动进行调整,这时,可能会在调整过程中清除掉进入程序的运行临时区的指针引入的内容。

针对上述情况,采取保护措施是必要的。保护方法有: 1)指针中设置选项用const;

2)对输入指针引入的内容进行同类数据结构转移保护。

4.用指针传递多个数据结构的常用两种方法 1)指针分层;

d D_Ds ds based(Ptr) d PtrA * d PtrB * *

d D_Ds1 ds likeds(RefDs1) d D_Ds2 ds likeds(RefDs2) *

/free

……

PtrA = %addr(D_Ds2) ; PtrB = %addr(D_Ds1) ;

2)归集多个数据结构,采用一个指针进行传递。

d D_Ds ds based(Ptr) d D_Ds1 likeds(RefDs1) d D_Ds2 likeds(RefDs2) 或者,

d D_Ds1 likeds(RefDs1) d D_Ds2 likeds(RefDs2) *

d c_String s 10000 varying based(Ptr) /free

…..

c_String = D_Ds1 + Ds2 ;

用指针分层方法的风险,因为这种方法是多个指针同层传参,即每一个指针引入一个数据结构,或复杂数据结构,比多个数据结构进行归集,然后用一个指针进行传参,前者的数据安全性比后者差。特别情况下,如果经过多级调用,且都是通过指针进行结果返回,可能出现数据丢失。 用数据归集方法的风险,存在前面数据结构中有变长字段。

四、违背常规指针的特殊用法

AS400 RPG高级语言已经经历几代的变更,实现技术也从开始的不规范,比如非同类属性数据可以相互传递,RPG语言逐渐趋于完善。但是在遗留下的RPG编程代码还有一些不规范的东西,不如在QAPI调用中,参数用指针引入的数据结构不规范。 这里总结非规范指针用法,用于参考。

1)在QAPI输入结构中,用一个字节定义替代指针类型的定义

我们在实际编程中发现,完全按照IBM文档对调用QAPI进行编程,有的地方很难通过,通过网上其它代码的参考,在D表中用一个字节定义替代指针类型的定义,在C表中用一个数据结构直接DS替代,调用QAPI的编程才能通过。比如:

调用QMHRTVM qapi,IBM文档是这样表述的:

Required Parameter Group: 1 Message information Output Char(*) 2 Length of message information Input Binary(4) 3 Format name Input Char( 4 Message identifier Input Char(7) 5 Qualified message file name Input Char(20) 6 Replacement data Input Char(*) 7 Length of replacement data Input Binary(4) 8 Replace substitution values Input Char(10)

9 Return format control characters Input Char(10)

10 Error code I/O Char(*)

Optional Parameter Group: 11 Retrieve Option Input Char(10) 12 CCSID to convert to Input Binary(4) 13 CCSID of replacement data Input Binary(4)

如果在rpgle程序代码中用这个数据结构对PR进行定义,对这个数据结构的第一项就很难编译通过。

在实际编程中,如果调用QMHRTVM 的PR如果是这样定义,即非规范指针定义,可以达到调用QMHRTVM的目的,代码如下:

dGetMsg pr extpgm('QMHRTVM')

d Receiver 1 d SizRcv 10i 0 const d Format 8 const d

MsgID 7 const d

Msgf 20 const d RplData 1 const d SizRplDta 10i 0 const d RplSubVal 10 const

d RtnCtls 10 const d ErrCod 10i 0 const

这里对PR定义的第一项,不是技术文档的指针类型,而是一个字符的类型替代,再在D表中进行定义,C表中的处理是这样的:

dGetSize ds d GetBytRtn 10i 0 d GetBytAvl 10i 0 /free

……

MsgId = 'E000001' ; // How much storage is needed for everything?

callp GetMsg( GetSize :%size(GetSize) :'RTVM0400' :MsgID :'CMMSGF RUN08141 '

:' ' :0 :'*NO' :'*NO'

:0) ;

//Allocate it and then call the API again

FmtPtr = %alloc(GetBytAvl) ; callp GetMsg(Fmt0400 :GetBytAvl :'RTVM0400' :MsgID :'CMMSGF RUN08141 '

:' ' :0 :'*NO' :'*NO' :0) ; 这里,数据结构‚GetSize‛直接使用PR定义的一个字节描述的‚Receiver‛,起到替代文档中的‚Message information Output Char(*)‛项。

后续的调用再用数据结构‚Fmt0400‛,直接使用‚Receiver‛项,起到相同的作用。

RPGLE高级练习(一):Procedure 2011-12-27 15:52:16 练习一

Module:Proc(程序代码)

* Procedure 内部调用 * Proc1(main) 调用 Proc2(nomain) * 默认值: main * H*Main Proc1 * d/copy QRPGLESRC2,RPGTXTSRC * d MyParm2 s 20 inz('Hello') d Rc s 7 inz * /free Rc = Proc1(MyParm2) ; *inlr = *on ; return ; /end-free * pProc1 b export d Proc1 pi 7 d MyParm2 20 * * /free //MyParm2 为Hello dsply MyParm2 ; // 调用 SubProcedure,Proc2 Rc = Proc2(MyParm2) ;

//返回值MyParm2 为Hello,RPGLE dsply MyParm2 ; return Rc ; /end-free

pProc1 e

Module:Proc1(程序代码)

H nomain d/copy QRPGLESRC2,RPGTXTSRC * pProc2 b export d Proc2 pi 7 d MyCallRet 20 * d MyParm2 s 20 inz('RPGLE') d Rc s 7 inz('0000000') d Erc s 7 inz('1111111') * /free monitor ; //MyParm2 为Hello dsply MyParm2 ; MyCallRet = 'Hello, RPGLE' ; dsply MyCallRet ; on-error ; Rc = Erc ; endmon ; return Rc ; /end-free pProc2 e

copybook,RPGTXTSRC .TXT代码:

d Proc1 pr 7 d MyCallRet 20 * d Proc2 pr 7 d MyCallRet 20

程序*PGM生成步骤:

1) 用crtrpgmod,或编辑选项15,编译Proc.rpgle,生成Proc.module; 2) 用crtrpgmod,或编辑选项15,编译Proc1.rpgle,生成Proc1.module; 3) 用CtrPgm,创建Proc.pgm

CRTPGM PGM(PINGY/PROC) MODULE(PINGY/PROC PINGY/PROC2)

RPGLE高级练习(二):Procedure 2012-01-04 15:19:50 服务程序:PROC12.rpgle代码

用crtrpgmod,或选项15,生成PROC12.module

* Procedure 内部调用 * Proc2(nomain) 被调用

* 默认值: nomain * H nomain * d Proc12 pr 7 d MyCallRet 20 * pProc12 b export d Proc12 pi 7 d MyCallRet 20 * d MyParm2 s 20 inz('RPGLE') d Rc s 7 inz('0000000') d Erc s 7 inz('1111111') * /free monitor ; MyCallRet = 'Hello, RPGLE' ; on-error ; Rc = Erc ; endmon ; //*inlr = *on ; return Rc ; /end-free pProc12 e

PROCSRV.BND代码:

STRPGMEXP SIGNATURE('PROCSRV') EXPORT SYMBOL('PROCSRV') EXPORT SYMBOL('PROC12') ENDPGMEXP

Service :PROCSRV.module代码: /free

Return ; /end-free

生成PROCSRV.*SRVPGM命令:

CRTSRVPGM SRVPGM(MYLIB/PROCSRV) MODULE(MYLIB/PROCSRV MYLIB/PROC12) SRCFILE(MYLIB/QRPGLESRC2)

应用程序PROC2.rpgle代码:

* Procedure 内部调用 * Proc2(main) 调用 Proc12(nomain) * 默认值: main *

H*Main Proc2 H BNDDIR('MYBND') * d/copy QRPGLESRC2,RPGTXTSRC2 * d MyParm2 s 20 inz('Hello') d Rc s 7 inz * /free Rc = Proc11(MyParm2) ; *inlr = *on ; return ; /end-free * pProc11 b export d Proc11 pi 7 d MyParm2 20 * /free //MyParm2 为Hello dsply MyParm2 ; // 调用 SubProcedure,Proc2 Rc = Proc12(MyParm2) ; //MyParm2 为RPGLE dsply MyParm2 ; return Rc ; /end-free pProc11 e

用crtrpgmod,或选项15,生成PROC2.module

用CRTPGM,生成PROC2.PGM

CRTPGM PGM(/PROC2) MODULE(/PROC2)

创建BNDDIR:

CRTBNDDIR BNDDIR(MYBND)

加载服务程序到BNDDIR:

ADDBNDDIRE BNDDIR(MYLIB/MYBND)

OBJ((MYLIB/PROCSRV))

Service program . . . PROCSRV Name Library . . . . . . . . . *CURLIB

Module . . . . . . . . . . . . . > PROCSRV . . . . . >

. . > MYLIB Name, Name, generic*, *SRVPGM, *ALL

Library . . . . . . . . . . . > MYLIB Name, *LIBL, *CURLIB...

+ for more values > PROC12

> MYLIB

Export . . . . . . . . . . . . . *SRCFILE *SRCFILE, *ALL

Export source file . . . . . . . > QRPGLESRC2 Name, QSRVSRC

Library . . . . . . . . . . . > MYLIB Name, *LIBL, *CURLIB

Export source member . . . . . . *SRVPGM Name, *SRVPGM

Text 'description' . . . . . . . *BLANK

如果采用Export *SRCFILE默认参数,bnd文件PROCSRV.bnd是放在pf-src目录QRPGLESRC2下。

copybook:rpgtxtsrc2

d Proc11 pr 7 d MyCallRet 20 *

d Proc12 pr 7 extproc('PROC12') d MyCallRet 20

RPGLE高级练习(三):Procedure 在这个练习中,放人了我对项目实施的实际做法。 调用指针(pointer,*)有三个:

1)Ptr1为项目总体参数指针,用于项目统一参数,如交易日日期; 2)Ptr2为单元模块输入指针,即外部调用本模块时,用Ptr2传入参数; 2)Ptr3为单元模块输出指针,即本单元模块处理完毕后,用Ptr3输出结果。

这样就对项目规范起来,减少项目接口的复杂性。所有的参数定义,如一般通用变量、ds结构等定义,都放在copybook中,这样可以保持所有输入输出参数在整个项目中能够保持唯一。

H nomain H OPTION(*SHOWCPY)

* /copy QTXTSRC,MSGID /copy QTXTSRC,MYDS

* d Proc13 pr 7

d Ptr1 * d Ptr2 * d Ptr3 *

* pProc13 b export d Proc13 pi 7

d Ptr1 * d Ptr2 * d Ptr3 *

* d

MyParm2 s 20 based(Prt2) d Hello s 20 inz('Hello, ')

*

/free

monitor ; If 1 ; MyParm2 = %trim(Hello) + ' ' + %trim(MyParm2) ; = %addr(MyParm2) ; Else ; = Ercode ; EndIf ;

on-error ; Ercode ; endmon ;

return Rc ; /end-free pProc13 e H*Main

Proc3 H option(*showcpy) H BNDDIR('MYBND') H*DFTACTGRP(*NO) *

/copy QTXTSRC,MSGID /copy QTXTSRC,MYDS /copy QTXTSRC,PRDS * Ds_Parms.Parm1

Rc = Ptr3 Rc =

d MyParm2 s 20 inz('RPGLE')

*

/free

Ptr2 = %addr(MyParm2) ; Rc = Proc13(Ptr1:Ptr2:Ptr3) ; If Rc = RcInz ; dsply Rc ; Else ; dsply Rc ; EndIf ;

*inlr = *on ; s return ;

/end-free bndfile:

QSRVSRC,PROCSRV:

STRPGMEXP SIGNATURE('PROCSRV') EXPORT SYMBOL('PROCSRV') EXPORT SYMBOL('PROC12') EXPORT SYMBOL('PROC13') ENDPGMEXP

生成PROCSRV.*SRVPGM命令:

CRTSRVPGM SRVPGM(MYLIB/PROCSRV)

MODULE(MYLIB/PROCSRV MYLIB/PROC12 MYLIB/PROC13) SRCFILE(MYLIB/QSRVSRC) copybooks:

MSGID.RPGLE : RcInz c '0000000' d Ercode c '1111111' * Rc s 7 inz('0000000') MYDS.RPGLE :

d Ptr1 s * d Ptr2 s * d Ptr3 s * d d

*

*Ds_Parms

d Ds_Parms Ds qualified d Parm1 5p 2 inz(1) d Parm2 5p 2 inz(2) d Parm3 5p 2 inz(3) PRDS.RPGLE :

dProc13 pr 7 extproc('PROC13') d Ptr1 *

d Ptr2 *

d Ptr3 * bnddir:

MYBND.*BNDDIR :

PROCSRV *SRVPGM ITSMXH

RPGLE高级练习(四)- 项目快速实施

前面练习我已经提到过RPGLE编程采用的程序架构,在实际项目实施过程中,如果对procedure不熟悉,勉强按procedure思路进行项目实施,会有很多麻烦,会造成拖延项目实施进度。

我总结出一套可操作的项目编程架构。

1) 按照练习(三)的思路,调整单元模块入口点;用*entry代替prototype原型,Pr原型入口描述,即在copybook中直接定义入口:

C *entry plist

C parm Ptr1 C parm Ptr2 C parm Ptr3

2) 在定义Pr的copybook中对每一个Pr的定义先采用关键字extpgm进行定义,

以方便单元编程程序员可以用CRTRPGPGM,即选项14,直接产生可执行目标代码,减少其它中间环节,就可以直接进行测试,debug。

3) 如果采用module项目实施,在每一个程序单元都做完测试后,可开始集成服务程序,*SRVPGM,这时把Pr定义的extpgm改为extproc键字,用CRTRPGMOD,即15,进行生成*module,然后再集成服务程序,*SRVPGM。

4) 用MSGFILE定义项目所有的msg信息;

5) 用QAPI写成例程,放人copybook中;用这个例程在单元程序处理过程中获取相应msgid代码的明文,用于写入物理文件pf进行保存,用于项目维护;或用另外一个copybook中的例程把msg明文返回给显示终端上。

6) 尽可能地把所有通用程序变量定义,或通用程序代码,都放人copybook中。 注:

1) 上面做法不能用于多线程应用环境,因为入口的问题。

2) 采用extpgm与extproc在H表中会有不同的键字定义,在项目实施过程中要对不同阶段的程序采用相应copybook中对H表的定义。

四个高级400成员需明白的概念和问题 问题:

1)*pgm与*module的差别?

2)pr extpgm定义的pgm是*pgm?还是*module?怎样调用? 3)bnddir的作用?

4)pr extpgm与pr extproc定义使用的区别? 答:

1)*pgm与*module的差别?

*pgm是*module+可执行环境,可以在libl中被程序call或被callp。

*mdule可以在三种环境被调用执行:a)*pgm(*module+可执行环境);b)bnddir中的*module;c)bndidr下通过*srvpgm的symbol链接的*module。

2)pr extpgm定义的pgm是*pgm?还是*module?怎样调用?

pr extpgm定义可以libl下的*pgm;或bnddir的*module;或bndidr下通过*srvpgm的symbol链接的*module,可以在libl中被程序call,或被callp在libl下调用,或bnddir下作为*module被调用。

3)bnddir的作用?

a) bnddir是一个路径,有类似libl的功能; b)bndidr是一个可执行的shell;

c)H表bnddir可以声明多个路径,如:H bnddir('MyBndDir1':'Tools':...) d) H表bnddir路径,在*module生成时不起作为,但使用相关的路径,要在编译参数bnddir中声明所用到的路径。H表bnddir只对生成后的路径起作。

4)pr extpgm与pr extproc定义使用的区别?

a)procedure定义和概念:procedure有P B ....P E定义+prototype原型定义; b)pr extpgm只能用在外部的*pgm或bndidr下的module;且没有P B ....P E定义,H表中只能main定义(默认值),且有*entry+plist,或prototype原型定义,是PEP处理单元。

c)pr extproc只能用在bnddir下的*module,或通过*srvpgm链接的*module(procedure);H表中可以main,或nomain定义。

如何判断srvpgm中的模块是否能debug跟进去?

请问一下,如何判断srvpgm中的模块是否能debug跟进去? 答:

1)strdbg 程序名;

2)进入debug模式后,按F14(Shift2),在‚Work with Module List‛窗口下,在Opt选项下键入1,添加;‚program/module‛下输入*srvpgm的名称,即debug的*pgm调用的*srvpgm名;‚Type‛选项下,输入*srvpgm。在module的*srvpgm的内容添加进来后,再移动光标,在指定栏,用选项1进入,进入*srvpgm的代码内容,再设置断点;.....

案例分析:ODP概念与编程应用、事务处理

案例分析:ODP概念与编程应用、事务处理 2011-06-17 12:32:42 一、 案例描述:

一个处理流有三个处理步骤: 步骤1:

用一个CL处理OVRDBF和OPNQRYF,对文件MyFile进行记录条件筛选。然后再调用RPGLE程序RPG1,对文件MyFile进行读操作。 步骤2:

RPGLE程序RPG1对文件MyFile进行读操作,循环读出MyFile的记录后,用一个字段的

信息,作为参数传递个RPGLE程序RPG2。 步骤3:

RPGLE程序RPG3把RPG2传入的参数,作为键字,对文件MyFile进行检索。如果检索到,进行修改记录,然后更新记录。

备注:文件MyFile在处理流过程下都处在事务处理制约中。 二、 事务处理环境准备和相应代码

1) 创建事务处理日志

步骤一:

在MyLib目录下,用CRTJRNRCV创建日志接收器MyJrnRcv: CRTJRNRCV JRNRCV(MYLIB/MYJRNRCV)

步骤二:

在MyLib目录下,用CRTJRN创建日志文件MyJrn: CRTJRN JRN(MYLIB/MYJRN) JRNRCV(MYLIB/MYJRNRCV) 步骤三:

把文件MyFile加载到事务处理MyJrn下:

STRJRNPF FILE(MYLIB/MYFILE) JRN(MYLIB/MYJRN)

2) CL代码:

PGM DCL VAR(&SLTSTM) TYPE(*CHAR) LEN(30) CHGVAR VAR(&SLTSTM) VALUE('BACXMC=''DY0101P04''') STRCMTCTL LCKLVL(*CHG) CMTSCOPE(*JOB) DFTJRN(MYLIB/MYJRN) MONMSG MSGID(CPF0000) OVRDBF FILE(MYFILE) TOFILE(MYLIB/MYFILE) + SECURE(*YES) OVRSCOPE(*JOB) SHARE(*YES) + OPNSCOPE(*JOB) MONMSG MSGID(CPF0000) OPNQRYF FILE((MYLIB/MYFILE)) OPTION(*ALL) + QRYSLT(&SLTSTM) MONMSG MSGID(CPF0000) CALL PGM(RPG1) ENDCMTCTL ENDPGM 3) RPG1代码:

H OPTION(*ShowCpy:*NODEBUGIO) * FMyFile if e disk F recno(rrn) * drpg2 pr extpgm('RPG2') d ckey 5 0 * d rrn s 5 0 inz /free //读出第100位置的记录

setll 100 rec ; read rec ;

callp rpg2(rrn) ; *inlr = *on ; return ; /end-free 4) RPG2代码:

三、 1) 2) H OPTION(*ShowCpy:*NODEBUGIO) * FBBCSB uf e disk * d rrn s 5 0 * c *entry plist c parm rrn /free setll rrn rec ; read rec ; // 修改记录内容 update rec ; *inlr = *on ; return ; /end-free

问题分析和解决

问题发生:

在准备好上述事务处理环境和代码后,在测试过程中,当运行RPG1时,os系统会报msg:Message . . . . : I/O error CPF5040 was detected in file BBCSB (C G D F). Cause . . . . . : The RPG procedure RPG1 in program PINGY/RPG1 received the message CPF5040 at statement 19 while performing I/O operation SETLL on file

BBCSB. The actual file is PINGY/BBCSB(BBCSB).

或者,os会报msg:CPF4293(如果没有setll语句和rrn)、CPF5119(如果没有setll语句)。

分析和解决问题

这一个处理处理流一共经过三个处理步骤。CL程序对文件MyFile进行记录条件筛选,然后对MyFile文件进行OVRDBF,即MyFile文件仅保留满足条件的记录,同时MyFile的ODP处在打开状态。因为OVRDBF对MyFile用SHARE(*YES)进行约束,即Myfile的ODP被打开后,后续的处理步骤中,凡是使用MyFile文件的操作,都保留在同一条ODP路径下。

然而,RPG1,和RPG2对同一路径下的文件MyFile又必须使用不同的ODP打开属性,即RPG1使用仅输入模式的ODP,IF;RPG2使用可更新模式的ODP,UF。这样,步骤一CL的用SHARE(*YES)对后续使用同一条ODP路径的制约就发生了矛盾。所以,运行中OS就会报上述不同的MSG信息。

只有把步骤一CL处理用OVRDBF对MyFile进行记录筛选后的ODP制约条件SHARE(*YES)改为SHARE(*NO)后,即不对ODP路径进行唯一性操作,这个处理流在不同处理步骤对文件MyFile,可以使用不同属性的ODP,对MyFile进行不同类型的记

录操作。

四、 结论(ODP概念)

在AS400平台下,

1) CL语言中,OVRDBF是ODP创建,DLTOVR 是ODP的关闭;

2) RPGLE中F表对文件的定义和使用就是ODP的创建,键字close,或*inlr=*on,或

者关闭AG,是对F表定义的文件已经打开ODP路径进行关闭。

3) 在处理流的过程中,对相同路径下的文件,比如MyLib/MyFile,进行不同属性的

操作,必须要使用不同属性的ODP,即ODP的打开数是根据操作属性不同而定。

BNDDIR>服务程序>Service:C函数>随机数

RPGLE例子(二):BNDDIR>服务程序>Service:C 随机函数>随机数 要求:在数字1000000和数字5000000直接产生一个随机数。 步骤一、

TC.cle:(ile环境下C语言) #include #include #include

extern int RANF(int start, int end) { int j, result; srand((unsigned)time(NULL)); j = rand()%(end - start); result = j+start; return result; }

这里TC的后缀必须是cle,或cpple,即ile环境下的语言后缀。 用CRTCMOD/CRTCPPMOD指定生成到MyLib下。

步骤二、

用RTVBNDSRC 命令在MyLib目录的QBNDSRC下,生成TC(BND)文件: RTVBNDSRC MODULE(MyLib/TC) SRCFILE(Mylib/QBNDSRC) SRCMBR(TC)

生成结果,即TC(BND)文件的内容:

STRPGMEXP PGMLVL(*CURRENT) /*********************************************************/ /* *MODULE TC MYLIB 11/06/15 17:09:04 */ /*********************************************************/

EXPORT SYMBOL(\"RANF\") ENDPGMEXP

步骤三、

用命令CRTSRVPGM生成服务程序TOOLS,*SRVPGM: CRTSRVPGM SRVPGM(MYLIB/TOOLS) MODULE(MYLIB/TC) SRCFILE(MYLIB/QBNDSRC)

SRCMBR(TC)

步骤四、

用CRTBNDDIR生成MYLIB/BTEST BNDDIR:

CRTBNDDIR BNDDIR(MYLIB/BTEST) 加载:

ADDBNDDIRE BNDDIR(MYLIB/BTEST) OBJ((MYLIB/TOOLS))

步骤五、

测试程序TEST.rpgle:

H OPTION(*ShowCpy) h DFTACTGRP(*NO) H BNDDIR('BTEST') * d Randam pr 10i 0 extproc('RANF') d s_Start 10i 0 value d s_End 10i 0 value * d s_Start s 10i 0 inz(1000000) d s_End s 10i 0 inz(5000000) d s_Result s 10i 0 inz /free s_Result = Randam(s_Start:s_End) ; *inlr = *on ; return ; /end-free

用选项14直接在MYLIB下生成TEST,*PGM。 注意地方:

1) C语言生成module,作为服务程序的一个service,C语言的后缀必须是CLE,或CPPLE。 2) C语言的原型接口是int时,调用程序rpgle的原型定义必须是10i 0。

RPGLE例子:指针,服务程序,BNDDIR

本例子演示RPGLE使用服务程序(*SRVPGM)通过指针传递参数进行调用。调用程序可以通过bnddir对服务程序进行直接调用,无需服务程序与子系统直接关联。

服务程序:TSRV.*SRVPGM

服务程序下挂服务应用程序M1.*Module BNDDIR:BTEST

测试程序:TEST.*PGM

M1.RPGLE:(用15编译成M1.*Module,放在MyLib目录下。) d M1 Pr d Ptr1 * d Ptr2 * * d C_Str s 20

d C_String s 20 based(Ptr1) d Ptr1 s * d Ptr2 s * * c *entry plist c parm Ptr1 c parm Ptr2 /free C_Str = C_String ; C_Str = 'Hello! ' + C_Str ; Ptr2 = %addr(C_Str) ;

*inlr = *on ; return ; /end-free

服务程序TSRV.rpgle:(用15编译成TSRV.*Module,放在MyLib目录下) /free

Return ; /end-free

服务程序TSRV下的服务应用程序定义bnd.rpgle文件:(放在MYLIB/BNDSRC目录下,无需编译) STRPGMEXP SIGNATURE('TSRV') EXPORT SYMBOL('M1') ENDPGMEXP

创建服务程序TSRV.*srvpgm:(编译结果放在MyLib目录下) CRTSRVPGM SRVPGM(MYLIB/TSRV) MODULE(MYLIB/TSRV MYLIB/M1) SRCFILE(MYLIB/BNDSRC)

创建BTEST. *BNDDIR(放在MyLib目录下): CRTBNDDIR BNDDIR(MYLIB/BTEST)

加载服务程序TSRV.*srvpgm到BTEST bnddir下: ADDBNDDIRE BNDDIR(MYLIB/BTEST) + OBJ((MYLIB/TSRV))

或者用WRKBNDDIR对BTEST.*bnddir进行交互操作。

测试程序TEST.rpgle:(用14进行编译,放在MYLIB下) H BNDDIR('BTEST') H DFTACTGRP(*NO) H OPTION(*ShowCpy) *

d M1 Pr ExtProc('M1') d Ptr1 *

d Ptr2 * * d C_Str s 20 inz('Body') d C_String s 20 based(Ptr2) d Ptr1 s * d Ptr2 s * * /free Ptr1 = %addr(C_Str) ; callp M1(Ptr1:Ptr2) ; *inlr = *on ;

return ;

/end-free

运行测试结果:

调用TEST,把字符串‚Body‛通过指针Ptr1带入到服务应用程序M1中,M1把字符串做处理,把字符串改为:‚Hello! Body‛。然后通过指针Ptr2返回到调用程序TEST。 BINDDIR技术:概念、使用 2010-11-23 16:22:45 binddir是400下一个obj类型,通过crtbnddir进行创建。

binddir的作用是把*module、*srvpgm类型的obj进行装载。装载可以通过1)用ADDBNDDIRE进行cl脚本加载;2)用wrkbnddir,进行交互装载。用编译参数指定bnddir仅仅对编译代码中调用外部module/s已经装载到binddir之下的名称指定。

binddir的作用,把不是最终执行代码*modules/*srvpgm装载到binddir后,相当于对*modules/*srvpgm套上一个可执行的shell。只有经过装在到binddir下的*modules/*srvpgm才能被调用,即可执行。

只有装载到binddir,或通过14选项进行编译的*modules才可以被调用执行。

如果某个*module被装载到binddir后被其它pgm用callp进行调用。如果这个module被改变,被调用的pgm就要重新对新编译的module进行重新链接,即把编译后的module的地址(pointer)更新到pgm调用的系统表中。

只有通过updpgm对pgm进行module新的地址pointer进行更新,更新后的pgm才可以对新的module进行callp调用。

我们马上会想到,如果有N个pgm对一个module进行调用,如果这个module被修改重新编译,不是就要对N个pgm进行updpgm进行重新pointer更新吗?答案是肯定的。

如果有这样情况发生,我们会发现如果用binddir单独装载module,就会造成一旦这个module进行修改重新编译,就要对调用它的所有pgm进行更新。我们在开发过程中就要不断的记录所有调用这个module的pgm,一旦这个module被修改重新编译,我们就要用cl统一进行updpgm。

IBM针对这个计算瓶颈采用了一个有效的解决方法,那就是srvpgm,一个os400下obj的类型。 Srvpgm一方面通过符号(symbol)定义关联每一个module的链接,即pointer地址。另一方面,一个调用这个srvpgm下module的外部调用程序pgm,通过binddir把pgm与binddi下的srvpgm的地址pointer进行编译链接,srvpgm就是通过两个绝对地址进行转换,即符号symbol对应srvpgm下的modules,srvpgm对应调用的pgm。这样,一旦srvpgm下的modules进行从新编译,只要对srvpgm和相关的module进行updsrvpgm就可以了。因为外部pgm通过binddir与srvpgm的地址链接没有必要更新,所以无需对pgm与srvpgm进行重新链接操作,即无需重新编译pgm

因为pgm对srvpgm的链接是固定的,所以,在updsrvpgm对.bnd文件使用时,不能改变srvpgm下modules的符号symbol位置,即不能改变.bnd文件Symbol在文件中的顺序或位置。

Binddir的使用:

1) 装载modules/*srvpgm;

2)外部pgm代码用H表定义,如H bnddir(’MyBndDir’)。

顺便说一下,如果对srvpgm下的module进行debug连调,即通过一个pgm与被调用的module进行debug,在strdbg pgm后用f14装载这个srvpgm,装载类型是*srvpgm。debug窗下就会显示这个srpgm下所有的modules,然后再用5选项进入到需要debug的module中设置断点。

RPGIV编程问题百解(35)服务程序(SRVPGM)创建 CRVPGM: module方式创建,假设要创建STEST服务程序 一个服务程序(SRVPGM)创建命令:

===> CRTSRVPGM SRVPGM(STEST) MODULE(MYLIB/STEST MYLIB/STEST1 MYLIB/STEST2) SRCF ILE(MYLIB/QBNDSRC)

STEST服务程序下包括STEST1、STEST2服务应用程序(service):

1) STEST.rpgle服务程序代码: /free

Retrun; /end-free

2) STEST1.rpgle代码: H

nomain h option(*showcpy)

*

/include mylib/qcpysrc,STESTPR

*

P stest1 B export d stest1 PI 10a d ymd 8p 0 const *

d retval s 10a d stest1 s 10a *

/free

monitor; retval = %char( %date(ymd:*iso) : *USA ); on-error;

retval = 'ERROR';

endmon;

*inlr = *on; return retval ;

/end-free p

stest1 e

3) STEST2.rpgle代码: H

nomain h

option(*showcpy)

*

/include pingy/qcpysrc,STESTPR * P

stest2 B d

stest2 PI

d s@string * d

stest2 s d

retval s

*

/free

returnval = %trim(s@string) + ' '+ 'Hello, Body!';

*on; return retval; /end-free p stest2 e

export 100a 100a const 100a 100a *inlr =

4) COPY book:/include mylib/qcpysrc,STESTPR

d Stest1 PR 10A extproc('STEST1') d c@date 8p const

*

d Stest2 PR 100A extproc('STEST2') d c@string 100a const

5) SRCFILE(MYLIB/QBNDSRC)下应包括STEST.BND文件,其内容: STRPGMEXP SIGNATURE('STEST') EXPORT SYMBOL('STEST1') EXPORT SYMBOL('STEST2')

ENDPGMEXP

以上三段程序代码需在用CRTSRVPGM之前,创建成*module代码形式。

创建后的STEST,*SRVPGM服务程序,用一个binddir,STEST挂起。 1) 创建一个binddir: CRTBNDDIR

2) 用wrkbnddir把STEST挂在STEST,binddir下。

做完上述步骤,就可以在其它rpgle程序中引用,如mypgm.rpgle,代码: H BNDDIR(‘STEST’) *

d Stest2 PR 100A extproc('STEST2') d c@string 100a const *这里也可以统一用/copy 文件定义。建议用/copy 文件。 D String s 100a d c@String s 100a /free …..

String2 = STEST2(c@String1); ….. 补充说明1):如果一个service定义PI:如STEST2.rpgle P

stest2 B export

d stest2 PI 100a …. d

stest2 s 100a d

retval s 100a

0

……

return retval;

这个procedure,的输出是可以export,在PI输出的100A,在d表中定义 D stest2 s 100a 用return retval;来体现。

如果没有定义export和PI后的输出属性,且仅return;,返回值只能用PI/PR中的原型接口(prototype)中。PI/PR中的定义必须一致。这时在mypgm.rpgle中使用STEST2,service,只能 Callp STEST2(c@String);

补充说明2):

如果STEST11.rpgle和STEST2.reple对F表定义,且操作,必须去掉H表中的nomain。没有人为定义main,或nomain,默认值是main。

也可以在STEST.rpgle服务程序中定义F表,对其操作。STEST11.rpgle和STEST2.reple的H表中定义nomain,相当于这STEST1和STEST2两个services都是subprocedures,也就是相当于我们熟悉的subroutinee,即begsr/endsr中的代码。

补充说明3):

如果把STEST1.rpgle和STEST2.rpgle生成modules后再用crtpgm生成PGM在SRVPGM中引用,这时在CRTSRVPG中BNDSRVPGM选项定义STEST1.pgm和STEST2.pgm。

补充说明4):

如果STEST1.rpgle和STEST2.rpgle不是用PI定义原型接口(prototype),而是用传统的Plist定义的接口,这时引用程序mypgm.rpgle的PR部分需把extproc键字改为extpgm定义引用的程序名。PR部分按照plist的parm个数和属性来定义。

补充说明5): 如果STEST1(subprocedure)和STEST2(subprocedure)都是在STEST.rpgle代码中,然后生成module或pgm,STEST.rpgle在MYLIB/QSRVSRC目录下在CRTSRVPGM中则需: Service program . . . . . . . . > STEST Library . . . . . . . . . . . *CURLIB Module . . . . . . . . . . . . . > STEST Library . . . . . . . . . . . > MYLIB

+ for more values

*LIBL

Export . . . . . . . . . . . . . > *ALL Export source file . . . . . . . QSRVSRC Library . . . . . . . . . . . *LIBL Export source member . . . . . . *SRVPGM Text 'description' . . . . . . . *BLANK 或,

Bind service program . . . . . . STEST

Library . . . . . . . . . . . MYLIB 生成后的STEST,*SRVPG与使用bnd文件生成的STEST,*SRVPG差别:

1) 后者可以用updsrvpgm对STEST服务程序中的STEST1,*module,或STEST2,*module单独进行更新操作。前者,则不能;

2) 前者无需bnd文件,后者必须有bnd文件。

3) 前者,STEST,*SRVPG中的STEST1和STEST2,subprocedures,循序是STEST.rpgle中的代码自然循序。后者,是STEST.BND中的export项的定义循序。

补充说明6):service可以是一个SRVPGM,*pgm;或一个SRVPGM下的一个subprocedure,*module。

用binddir创建srvpgm与不用binddir创建srvpgm的两种方法和差别 用binddir创建的srvpgm:

1)用binddir创建的svrpgm,顾名思义,需要一个binddir; 2)需要一个bind文件,.bnd;

3)通过这个binddir把bind文件定义的服务应用程序记在这个服务程序下。

4)服务应用程序可以是标准程序的编程格式,即无需定义接口原型,只用*entry定义。 5)服务程序与挂在其之下的服务应用程序必须是的module形式;

6)通过crtsrvpgm命令的参数项binddir,把各个服务应用程序module/s静态的bind在一起。 7)服务应用程序在bind文件中,即登记在binddir下这个服务程序的服务应用程序是有次序的,重新生成服务程序,若改变服务应用程序的在bind文件中的次序,如改变服务应用程序的命名后调整在bind文件中的位置,会造成调用这个服务应用程序的错误。如果一定要改变bind文件中服务应用程序的位置,方法一、保留原来次序位置,在bind文件的末尾增加新改名的这个服务应用程序;方法二、通过bind文件中的STRPGMEXP PGMLVL(*CURRENT)和STRPGMEXP PGMLVL(*PRV)定义节,调整重新命名的服务程序的位置。

8)在应用程序的生成过程中,通过binddir,找到相应的服务应用程序,然后静态地与这个服务应用程序bind在一起。

9)程序的备份和恢复过程,会丢失bind静态链。

10)用binddir生成的svrpgm可以通过wrkbnddir命令,交互式地往某个binddir下的svrpgm程序添加或删除service module。

不用binddir创建的srvpgm: 1)没有binddir;

2)没有bind文件,但是必须在服务程序中,通过PR节定义所有的服务应用程序;

3)服务应用程序在服务程序中的定义,没有次序问题,可以随意改变服务应用程序的位置;

4)服务应用程序的procedure可以与服务程序连体,或分开的;如果是分开,在crtsrvpgm时,把这个程序所包扩的所有modules bind在一起,生成服务程序。 5)服务程序在H表中,必须定义NoMain; 6)服务程序中可以只有D表,即定义节; 7)如果服务应用程序时的procedure用于生成moudle,procedure的H表,也必须定义NoMain; 8)因为RPGVI v6r1版本前,不允许NoMain的procedure定义文件,即使用F表,所以在非binddir的服务程序下,不允许服务应用程序使用标准编程格式,即使用*entry节代替原型接口。只能使用定义原型接口的PI的procedure,或由其生成的moudle。

9)在应用程序的生成过程中,编译器查找库列表中所有服务程序,第一个服务应用程序就与应用程序静态地bind在一起;所以多个同名的服务程序,或者服务应用程序要注意版本控制。

10)与上述的用binddir创建的服务程序一样,程序的备份和恢复,可能会丢失bind静态链。 11)没有用binddir生成的srvpgm,不能用wrkbnddir命令交互式地往某个binddir添加或删除service module。

RPGIV编程问题百解(16)/COPY 与/INCLUDE区别

/COPY是RPG历史的使用延续。/COPY不能用于SQLRPGLE,但是,/INCLUDE可以。 INCLUDE的用法有两种:

1)传统的用法: /EXEC SQL /INCLUDE /END-EXC

2)INCLUDE的另外一种用法:

C/EXEC SQL INCLUDE member-name C/END-EXE

第二种用法可以直接用在sqlrpgle的自由格式中。如: /free

......

EXEC SQL INCLUDE member-name ...... /end-free

include的member是TXT后缀的文本文件,它可以存放sql脚本,或粘贴其它sql脚本。 /copy和/include最大递归嵌套是32层。

RPG编程中使用屏幕光标定位技巧与用途

当我使用RPG编程一段时间后,我经常思考,怎样模范IBM OS/400操作界面?或者,我思考,在使用子文件时能否用光标定位某条记录的某项显示字段,然后做相应的操作? 答案是肯定的。

下面我就这方面的技巧分三个步骤做个编程介绍: 第一、 定义显示光标信息 第二、 算出光标坐标 第三、 使用光标技巧

第一、 定义显示光标信息

在RPG程序中定义显示文件的显示DS信息

fdsfile01 cf e workstn

f sfile(dspsfl:recno)

f infds(infds) *

d infds ds *

d row 370 370 d col 371 371 *

* 或用copy文件定义 * 定义变量

d ds

d halfwd 1 2b 0 d byte2 2 2 *

d curx s 2 0 inz d cury s 2 0 inz * /free

……

//第二、算出光标坐标 begsr subcur;

cury = 0; Halfwd = 0; Byte2 = row; cury = Halfwd;

//

curx = 0; Halfwd = 0; Byte2 = col; Curx = Halfwd; endsr;

//第三、使用光标技巧 //位置判断

Begsr Juspst; Exsr subcur; Select;

when cury =12 and (curx >31 and curx <34); exsr subpro1; when cury = 13 and curx = 32; exsr subpro2; endsl; endsr;

//或者 当按下F1或F4时,做相应处理 Begsr JusFun; Select;

when *in01 = *on ; exsr Juspst; when *in04 = *on; exsr Fun04; Endsl; Endsr;

/end-Free

RPG ILE服务程序创建三步曲 一. 概念

BNDDIR BNDDIR是指定存放modules和服务程序的路径。

CRTBNDDIR是定义一个符号指定存放modules和服务程序的路径。 指定BNDDIR的模式 BNDDIR可以通过CRTBNDDIR命令来指定;也可以通过程序定义来指定。程序

定义用法:

/IF DEFINED(*ILERPG)

H BNDDIR('APPLIB/ABNDSRC':'\"BND1.BND\"') /ENDIF

两者的区别在于,当一个bnddir是明确指定,即项目约定的,就可以在程序中直接指定。 程序控制行指定的bnddir不能覆盖命令行指定的bnddir。 二. 假设环境

在APPLIB下建立: /ABNDSRC /ASRVSRC /ARPGSRVSRC 3个Source PF

一个RPG服务程序SRV1,下挂两个服务应用程序,PGM1、PGM2。两个服务应用程序PGM1、PGM2是一般的编程模式;调用PGM1和PGM2用原型接口。 三. 创建三步曲

第一步:创建服务应用程序 1. 服务应用程序(service)

按一般RPG编程,在APPLIB/ARPGSRVSRC下编辑个服务应用程序,PGM1、PMG2。然后,PGM1和PGM2用15选项,CRTRPGMOD,在APPLIB下生成PGM1和PGM2 modules。

如果是其它非RPG代码,用CRTPGMMOD生成modules。 创建服务应用程序在RPGLE下有两种形式:

1)传统的rpgle程序,不过不生成pgm,而是module; 2)procedures,生成module。 2. BND文件

第二步:创建BND文件和BNDDIR

在APPLIB/ABNDSRC下,编辑一个后缀为BND1.BND的文本文件。内容: STRPGMEXP SIGNATURE('BND1') /* PGM1 */

EXPORT SYMBOL('PGM1') /* PGM2 */ EXPORT SYMBOL('PGM2') ENDPGMEXP 注意:

1) 在BND程序中指定的循序是相对固定的。因为SVRPGM与其服务应用程序间的关系是静态的连接关系。一旦生成SVRPGM与其服务应用程序的指针是固定的。一旦变更了循序,就要编译相关的程序,否则,就会调用失败。

2) 如果一定要把PGM1与PGM2的循序对调,可以采用,废弃PGM1用PGM3来代替,无需删除EXPORT SYMBOL('PGM1'),只要在EXPORT定义的最后一行加入EXPORT SYMBOL('PGM3')。或者,采用以下BND文件:

/* Current version of exports */

STRPGMEXP PGMLVL(*CURRENT) LVLCHK(*YES) SIGNATURE('BND1')

EXPORT SYMBOL(PGM2) /* PGM2 */ EXPORT SYMBOL('PGM1') /* PGM1 */ ENDPGMEXP

/* Previous version of exports */ STRPGMEXP PGMLVL(*PRV)

EXPORT SYMBOL(PGM1) /* PGM1 */ EXPORT SYMBOL('PGM1') /* PGM2 */

ENDPGMEXP

注意:带*CURRENT参数的STRPGMEXP PGMLVL只允许一个。*PRV的STRPGMEXP PGMLVL可以多个。意味着可以把多个前SVRPGM组合成一个新的SRVPGM。本例,BNDDIR名为BND1指向APPLIB。

在CRTSRVPGM中,这个bind member放在 export SRCMBR参数项中。存放这个bind member的目录放在export SRCFILE,即把系统默认参数QSRVSRC改为你存放bind member的SRC-PF目录名。

第三步:创建服务程序 3. 服务程序

在APPLIB/ASRVSRC下,编辑一个后缀为SRV1.RPGLE的文本文件。 内容:

/Free

Return ; /End-Free

创建完这个服务程序(server)后,用15编译选项编译成module放入指定的lib下。生成后的module名在使用CRTSRVPGM时,是参数SRVPGM的指定名。

然后用CRTSRVPGM SRV1,键入F4选项。在MODULE选项中,输入PGM1、PGM2;在BNDDIR中输入BND1。

编译参数MODULE由系统默认值*SRVPGM,改为*ALL。(创建方法一)

BNDDIR和相关的LIB参数填入已经存在的,或者本文开始的用CRTBNDDIR命令创建的bind directory。

以上三步已完成一个服务程序的创建过程。

在后续的对一个服务程序下的服务应用程序的维护,比如修改了这个服务应用程序,并且对这个服务应用程序重新生成了module后,只需对这个服务应用程序单独进行维护,可以用 UPDSRVPGM 对服务程序进行更新。

利用DLYJOB命令进行RPG ILE程序连调

在大型项目开发中,由于项目都是划分为多个模块进行相对开发。在项目后期进行模块间的连调阶段中,程序之间的测试参数传递,是连调过程中的一个非常重要环节。比如,比较好的项目设计实现中可以采用

callp(项目参数DS:模块参数DS:程序参数DS)模式

因此,模块程序间的连调,人工在命令行键入传递参数,就成为一项不现实的工作。 这里介绍利用delay命令进行RPG ILE程序连调技巧。 我们先回顾对RPG程序debug的步骤: 1) strdbg;

2) 切换到命令行,对debug的RPG进行调用:

Call RPGPGM(参数串)

在模块程序连调的时候,只有在上级调用程序驻留内存的时候,strdbg和call rpgpgm才会起到调试的作用。否则,就会失效。

在利用400系统命令DLYJOB(秒)进行连调时,把上级调用程序调用关系由指向rpgpgm,改为调用一个CL程序。

CL程序(CLPGM)组成:

DLYJOB(60) //60秒的延长时间

call rpgpgm(参数串)

在60秒的延长时间内,通过wrkactjob命令,查看CLPGM是否被驻留内存,如果被驻留内存,说明这个CL程序已经被调用。正处在dlyjob状态中。这时,把光标移动到这个绿屏记录下(wrkactjob屏中),按F11功能键两次,会出现jobname、username、jobnumber三个信息。记录下这三个数据。立即用STRSRVJOB命令,填入上面说的这三个数据,进行启动。接着,在命令行键入strdbg,进入debug环境,设置debug断点。一旦CL程序结束dlyjob命令后,调用rpgpgm,就可以对RPG程序进行带参数的调试。

设置延长时间,可以根据实际情况,进行设置合适的秒数。 这种400下连调方式的原理: Hold jobq, STRSRVJOB,

STRDBG program down the stack release jobq

DLYJOB的作用是Hold jobq。DLYJOB时间结束就是释放jobq,进入rpgpgm的调用。

这种方式同样适用于submitjob,出现'MSGW'的debug。

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- kqyc.cn 版权所有 赣ICP备2024042808号-2

违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务