|
I am calling the program from the Series i. I am debugging it from there, because we a live 24/7
Yes, I understand that. In technical terms:The problem that you've described is a result of invalid/incorrect memory allocation. The program that allocates the memory for the parameters hasn't allocated enough. The program that's referencing the memory is trying to read beyond the end of the allocation, and is reading data from a different allocation. That's why one parameter appears to be inside another one, because the first one is trying to address beyond the limit of the parameter.
This is how variables work:When a program starts up, it needs memory ("main storage") to store it's variables in. The compiler has generated code in your program that asks the operating system for memory. If you've declared a variable that's 30000 bytes long, the compiler says "hey operating system, I need 30000 bytes of main storage". The operating system says "sure, program! You can use 30000 bytes starting at address 12345."
The exception to this is input parameters. A program doesn't ask the operating system for memory for it's input parameters. Instead, it asks the prgoram that called it for memory for them. The program that called it provides the memory of the parameter that it passed -- thus the caller and the callee put their variables in the same spot in the computer's memory. This way, the caller can have data in that variable, and it'll be read by the callee. And the callee can change that data, and the caller will be able to see the new value when it regains control.
So that's how parameters work. Here's a trivial example: PGM DCL VAR(&PARM1) TYPE(*CHAR) LEN(10) CALL OTHERPGM PARM(&PARM1) ENDPGMWhen this CL program starts, it asks the operating system for 10 bytes of memory because &PARM1 is not an input parameter. The operating system might respond with (for example) "okay, you can have 10 bytes at address 1001". Now, anytime that the preceding CL program tries to access the &PARM1 variable, it'll reference the memory in positions 1001 - 1010 because that's where the operating system told it to put it's variable.
Indeed, a compiled program has no concept of variables. It's actually working directly with memory, because that's how computers think. Variables are just an abstraction to make it easier for human beings to work with memory.
Okay, so that CL program called OTHERPGM. OTHERPGM was an RPG program that's defined like this:
C *ENTRY PLIST C PARM PARMA 10When this RPG program runs, it does NOT ask the operating system for memory. Instead, it gets the address from the caller. Since the caller's variable was in position 1001, all the CL program really passed to the RPG was that number (1001). The RPG program put's it's PARMA variable in position 1001, and since it's also defined to be 10 long, it'll read positions 1001 - 1010 -- causing it to see the exact same data as the CL variable. In effect, the two variables "overlay" each other. Changes to one affect the other -- just like two overlaid variables in a data structure.
D ds D PARM1 1001 1010 D PARMA 1001 1010Except, of course, that your computer's main storage is probably billions of bytes long, and has lots and lots and lots of allocations, not just these two. But hopefully you get the idea.
Now let's look at your example: PGM (&usr &tnbr &pro &bol &po &emsg &rcnt + &lst1 &lst2 &lst3 &lst4) DCL &usr *CHAR 10 DCL &tnbr *CHAR 7 DCL &pro *CHAR 8 DCL &bol *CHAR 11 DCL &po *CHAR 12 DCL &emsg *CHAR 80 DCL &rcnt *CHAR 3 DCL &lst1 *CHAR 30000 DCL &lst2 *CHAR 30000 DCL &lst3 *CHAR 30000 DCL &lst4 *CHAR 30000 CALL INROLLXML (&usr &tnbr &pro &bol &po &emsg &rcnt + &lst1 &lst2 &lst3 &lst4) ENDPGMNow this CL program, (unlike my previous example) does NOT ask the operatign system for memory. All of it's variables are INPUT PARAMETERS, and as such, it uses the memory passed into it from the caller.
The same is true for the RPG program (presumably INROLLXML). It never asks the operating system for memory because the variables are all input parameters:
C *Entry Plist * User Name (Passed In) C Parm @@USR 10 * Transaction Number (Passed In) C Parm @@TNBR 7 * Probill Number (Passed In) C Parm @@PRO 8 * BOL Number (Passed Out) C Parm @@BOL 11 * PO Number (Passed Out) C Parm @@PO 12 * Error Message (Passed Out) C Parm @@EMSG 80 * Number of HTML Rows (Returned to Calling Pgm) C Parm @@RCNT 3 C Parm @@LST1 30000 C Parm @@LST2 30000 C Parm @@LST3 30000 C Parm @@LST4 30000Your complaint is that the @@EMSG field is overlaying the @@RCNT field. That means that whichever program allocated the memory for the @@EMSG field didn't allocate the full 80 bytes. It's not the RPG program here, because @@EMSG is an INPUT PARAMETER, so the program doesn't ask for memory.
It's not the CL program that called it, since that CL program doesn't ask for memory for the &emsg variable. It gets the memory address from IT'S CALLER.
Now, I'm sure you're not to call the CL program from the command-line with the CALL command. After all, you've already been asked if that's what's happening, and been pointed to the FAQ which talks about this. But, since you won't tell me how you're calling it, let's use the command line as an example so you can see how the pieces fit together. For example, let's say you typed the following:
CALL THECLPGM PARM('RICHARD' '1234567' '87654321' 'ABCDEFGHIJK' '210987654321' 'Error Message' 'CBA' 'ListVal1' 'ListVal2' 'ListVal3' 'ListVal4')In this situation, you're not passing variables to the call command. You're just typing values as part of one big field (the command-line field) and it's going into the operating system into a program named QCMD that interprets and executes commands.
QCMD will scan through the command, and figure out where the program name is. It'll figure out where each parameter starts and stops in the command string. For each parameter, it'll ask the operating system for memory to store the value of each parameter so that it can pass the addresses to the CL program.
Since it's the command line, not a CL program, there are no variables. How does it know how much memory to ask the operating system for? It has no clue what this memory will be used for later, and it has nothing to tell it how big each string can eventually be! So how does it know?
It has a set of rules. For character strings, if the value passed from the user is shorter than 32 bytes, it always allocates 32 bytes, put's the value at the start of those 32 bytes, and sets the rest of the space to blanks. If the value passed by the user is higher than 32 bytes, it uses the exact size that the user passed in.
Looking at the way I called your program, none of the parameters were longer than 32 bytes. So it allocates 32 bytes for all of them. It asks the operating system for 11 allocations, each 32 bytes long. The operating system finds free memory (most of the time when working with allocations this small, it'll find spaces that are consecutive in memory, since the system will have blocks of memory larger than 32 bytes available.)
So the operating system provides addresses 1001, 1033, 1065, 1097, 1129, 1161, 1193, 1125, 1257, 1289, 1321... all 32 bytes apart, and all have 32 bytes reserved to them. (These aren't real addresses, they're just examples I made up -- a real one is actually a 128-bit, very large, number, but the same principle applies).
If you go back to my data structure analogy, the command line (which doesn't know the name of the various parameters) has request this memory:
D ds D CMDLINE1 1001 1032 D CMDLINE2 1033 1064 D CMDLINE3 1065 1096 D CMDLINE4 1097 1128 D CMDLINE5 1129 1160 D CMDLINE6 1161 1192 D CMDLINE7 1193 1224 D CMDLINE8 1225 1256 D CMDLINE9 1257 1288 D CMDLINE10 1289 1320 D CMDLINE11 1321 1352Now it calls your CL program -- the one you posted. The CL program receives all of it's variables as input parameters. It doesn't ask the operating system for any memory. Instead, it asks it's called (the command line) for some memory addresses. It gets the ones I listed in my data structure, above.
However, the CL program does NOT define all of it's parameters as 32 chars like the command-line did. So even though they start at the same addresses, they address different bytes (sometimes less, sometimes more) within that range. Back to the data structure analogy, you have this:
Again, if you think of your computer's main storage as a data structure, and you understand that each CL variable uses the memory passed by the command line (it does not ask the operating system for memory or calculate it's own) but uses it's own internal definition for the data type and length, then you arrive at this data structure:
D ds D CMDLINE1 1001 1032 D &usr 1001 1010 D CMDLINE2 1033 1064 D &tnbr 1033 1039 D CMDLINE3 1065 1096 D &pro 1065 1072 D CMDLINE4 1097 1128 D &bol 1097 1107 D CMDLINE5 1129 1160 D &po 1129 1140 D CMDLINE6 1161 1192 D &emsg 1161 1240 D CMDLINE7 1193 1224 D &rcnt 1193 1195 D CMDLINE8 1225 1256 D &lst1 1225 31224 D CMDLINE9 1257 1288 D &lst2 1257 31256 D CMDLINE10 1289 1320 D &lst3 1289 31288 D CMDLINE11 1321 1352 D &lst4 1321 31319The first few parameters aren't a problem. But look at the &emsg and &rcnt ones that you said you were having problems with. Notice that &emsg goes from 1161-1240 (because the caller provided 1161 as the starting address, and it's 80 long). and &rcnt goes from 1193 to 1195 (again, because the caller passed 1193, and it's 3 long.) The problem is, 1193 and 1195 are WITHIN the range of 1161-1240, so now &rcnt appears to be WITHIN &emsg. This isn't &rcnt's fault, of course. &rcnt is doing nothing wrong -- but &emsg is reading more memory than the 32 bytes that were allocated to it. It's reading an extra 48 bytes which happen to contain &rcnt because the system happened to use the space after CMDLINE6 for &rcnt -- just by luck. (It doesn't HAVE to provide consecutive addresses, but it's very common.)
You'll also note similar problems with the &lst1-4 parameters. The system allocated 32 bytes for them, but they try to address 30000! That'll really cause problems if you try to use them, since the memory they overwrite won't be another variable, but'll be some other part of your program or will be unused memory, or might even be something used by the operating system, causing really dire consequences. (But only sometimes! Because the memory addresses can be different every time the OS allocates them! That means that you can test you program and all will be will, but it might cause some completely strange and unrelated result later, maybe even in production.)
Very very bad.But the CL program doesn't actually USE these variables, so you don't even see the problem in the CL program itself. It just passes them on to the RPG program. The RPG program will not ask for new memory for it's variables, but once again, it'll just use the addresses passed into it. These addresses will be the same ones that QCMD allocagted way back at the start... (That's why I need to understand how your CL program is called -- since it didn't allocate the memory, whomever called it did.)
The RPG program does the same thing the CL program did. It has it's own data types and definitions, but uses the starting addresses passed in as parmaeters. So that makes our data structure analogy look like this:
D ds D CMDLINE1 1001 1032 D &usr 1001 1010 D @@USR 1001 1010 D CMDLINE2 1033 1064 D &tnbr 1033 1039 D @@TNBR 1033 1039 D CMDLINE3 1065 1096 D &pro 1065 1072 D @@PRO 1065 1072 D CMDLINE4 1097 1128 D &bol 1097 1107 D @@BOL 1097 1107 D CMDLINE5 1129 1160 D &po 1129 1140 D @@PO 1129 1140 D CMDLINE6 1161 1192 D &emsg 1161 1220 D @@EMSG 1161 1220 D CMDLINE7 1193 1224 D &rcnt 1193 1195 D CMDLINE8 1225 1256 D &lst1 1225 31224 D @@LST 1225 31224 D CMDLINE9 1257 1288 D &lst2 1257 31256 D @@LST2 1257 31256 D CMDLINE10 1289 1320 D &lst3 1289 31288 D @@LST3 1289 31288 D CMDLINE11 1321 1352 D &lst4 1321 31319 D @@LST4 1321 31319It doesn't really change anything. @@RCNT will appear to be within @@EMSG because CMDLINE6 wasn't large enough for hold @@EMSG. Same is true of @@LST1 - 4. The program that actually allocated the memory (in this example, QCMD) didn't allocate enough memory to store the strings, so they ended up extending beyond their allocations into other areas of memory.
How can you fix the problem? Simple. you have to FORCE IT to allocate enough memory for each parameter. The way you do that is by calling the program (either the CL or the RPG directly) from another program that has variables that are at LEAST as long as the largest definition that will share that memory.
One easy way is to copy your CL program to another member. Change the variables so that they're NOT input parms. Instead, set each value within the CL program using a CHGVAR command. That way, the CL program will take care of allocating the memory -- since they're not input parms, and when it calls the other CL program (or the RPG directly) it'll pass large enough blocks of memory that nothing will exceed the amount allocated.
Note that this is how parameters have always worked -- even back on the S/38! The fact that your original RPG/400 program worked was just blind luck. The system HAPPENED to (by chance) allocate the memory so that the extra memory that it addressed happened to be unused memory instead of the memory where &RCNT is stored. You just got lucky -- there were no guarantees that it would happen that way.
As an Amazon Associate we earn from qualifying purchases.
This mailing list archive is Copyright 1997-2025 by midrange.com and David Gibbs as a compilation work. Use of the archive is restricted to research of a business or technical nature. Any other uses are prohibited. Full details are available on our policy page. If you have questions about this, please contact [javascript protected email address].
Operating expenses for this site are earned using the Amazon Associate program and Google Adsense.