In native COBOL we used a "CALL progname" statement in processing through a set of programs by looping through and setting the variable progname, a PIC X(n), to each program name in turn.
How do we implement the same functionality in managed COBOL? "INVOKE progname"? What would be the content of progname?
What is it that you are trying to call a COBOL program (program-id) or a method in a COBOL class (class-id)?
If it is a COBOL program then you can use a CALL statement just like in native including a dynamic call.
If it is a class then you can invoke a method using invoke but doing this dynamically is much more difficult.
I believe I have an example if this what you need to do.
All of our native COBOL programs (one program per .dll) have been converted to COBOL classes, one method (METHOD-ID InstanceMethod in every class) per class, and all classes in a single project (.dll) called COBOLPrograms.
What we are trying to implement (from a native Console COBOL program):
02 FILLER PIC 9(3) VALUE 4.
02 FILLER PIC X(6) VALUE "AFCT1P".
02 FILLER PIC X(6) VALUE "WYBAKP".
02 FILLER PIC X(6) VALUE "WYFTPP".
02 FILLER PIC X(6) VALUE "WYFTPP".
01 PROGRAM-TABLE REDEFINES PROGRAM-LIST.
02 PROGRAM-STEPS PIC 9(3).
02 PROGRAM-NAME OCCURS 4 TIMES PIC X(06).
MOVE 1 TO PARM-STEP.
MOVE PROGRAM-NAME (PARM-STEP) TO PARM-PROGRAM.
CALL PARM-PROGRAM USING PARM-CONTROL.
IF PARM-STEP < PROGRAM-STEPS
ADD 1 TO PARM-STEP
GO TO 002-CALL-LOOP.
is a COBOL "driver" program/class started from C# code-behind of an .aspx program that will accept a list of COBOL programs/classes (created in the .aspx program) to execute in list order. The idea is that we would create a single COBOL Run Unit in the .aspx program, start the "driver" program passing it the list, and the .aspx program would be done. The "driver" program would "STOP RUN" when it finished the list.
What we have so far:
C# code ---------------------------
public class RunUnit
public static void TaskDriver(string PROGRAM-LIST)
MicroFocus.COBOL.RuntimeServices.RunUnit myRunUnit = new MicroFocus.COBOL.RuntimeServices.RunUnit();
TaskDriver myCobol = new TaskDriver();
myRunUnit.StopRun(); //Free Resources
Driver code ---------------------
*LOOP run each program (DLL Class Method)
perform varying X from 1 by 1 until X > PROGRAM-STEPS
* Program to run
Set sProgram to PROGRAM-NAME (X)
* Want to make following dynamic by using "sProgram" instead of
* class name AFCT1P
set myobj to new AFCT1P
invoke myObj::InstanceMethod(ss) returning ss
move "ERROR" To ss
What is your suggestion? Are we anywhere near the "right track"?
There isn't really an equivalent of COBOL's dynamic call when dealing with methods. The code in our run-time system that processes dynamic calls makes use of reflection APIs to identify the required call target and then to invoke it, but this really only works when the target is a procedural COBOL program or an entry point in a procedural COBOL program.
It would be possible to write your own code to do similar things using reflection APIs, but it's not really recommended as it's slow and not very intuitive.
I suppose my first question would be why have the programs been converted in this way from program-id to class-id? If there's no compelling reason for this, they could be left as procedural programs, and the driver program could happily use the COBOL CALL statement. Procedural COBOL can happily co-exist with class-structured programs either in the same project or in different projects.
If they really do need to be transformed explicitly into classes (note that under the covers procedural COBOL is in fact compiled into a class in which the procedure division is an instance method), then a possible approach to this sort of dynamic launching might be to set up an array of delegates to the various programs and to launch one of these delegates based on an index value.
The reason for converting from PROGRAM-ID to class-id was to make them callable from C# code-behind of a .aspx program running under IIS. The programmer that started this project says he kept getting "un-registered .dll" errors when he tried to call the PROGRAM-ID versions. The error went away when he converted them to class-id.
We wanted to preserve as much as possible the screen-oriented experience that our users have had for 20 years running these 50 tasks (our users hate change, they tend to freeze when they encounter anything they haven't seen 15 dozen times before). The 50 native COBOL console .exe programs, using SCREEN SECTIONs for task parameter entry, each called varying sequences of 140 native COBOL .dlls. So we tried converting into 50 console .exe programs to .aspx web programs calling the .dlls (recompiled as managed code, of course). That is where he started getting the "unregistered" error. So we converted all 140 .dlls to class-id.
But now in the new production environment we have been forced to move into we are finding that IIS is too flakey/unstable to maintain session/connection when a called .dll (within a Run Unit) takes several minutes to return. Sometimes the .aspx program is there and calls the next .dll, sometimes it is just "gone" and the task execution halts with no notification or indication to the user (except the user gets suspicious when he's two hours into a historically 15-minute task).
Hence this new effort: Keep the .aspx programs but have them make a single call to a COBOL "task driver" program in a Run Unit, passing it a list of which .dlls to execute, in the desired order, along with all task parameters. And the .aspx programs won't wait for the "task Driver" to return.
We were getting the feeling from earlier responses that the "dynamic calls" wouldn't work with class/method, and we didn't want to go back through all 140 .dlls (at this time, anyway). So we are going to use an EVALUATE with 140 WHEN clauses to match the possible program names in the passed list and use a regular INVOKE for each. Its inelegant and brute-force-ish, but we've gotten a test case task converted and it is working.
Thanks for everyone's attention and patience.