Michael Hendrick
Systems Support Engineer, Languages
February 1992
This article explains how Microsoft® COBOL programs can pass parameters to and receive parameters from Microsoft C programs. It assumes you have a basic understanding of the COBOL and C languages.
Microsoft COBOL supports calls to routines written in Microsoft C, FORTRAN, Pascal, and Assembler. This article describes the necessary syntax for calling Microsoft C routines and contains a series of examples demonstrating the interlanguage capabilities between COBOL and C. The sample programs apply to the following Microsoft products:
The C interface to COBOL utilizes the standard C extern statement. The following are the recommended steps for using this statement to execute a mixed-language CALL from C:
Note: When compiling, if the /Gc compiler directive is used (the /Gc option causes all functions in the module to use the FORTRAN/Pascal naming and CALLing conventions), then the cdecl keyword should be used when the COBOL function is declared (because COBOL uses the C CALLing convention, not the Pascal CALLing convention).
C automatically translates array names into addresses. Therefore, arrays are automatically passed by reference and don't need the * (asterisk) operator.
The default for C is to pass all arrays by reference (near or far, depending on the memory model) and all other data types by value. C uses far data pointers for compact, large, and huge models, and near data pointers for small and medium models.
The C default is to pass everything except arrays by value.
Arrays can be passed by value only if they are declared as the only member of a structure. The following example passes all 100 bytes of x directly to the C function test():
struct x_struct {int x[100]) xs;
.
.
.
test(xs)
In C, passing a pointer to an object is the equivalent of passing the object itself by reference. Within the CALLed function, each reference to the parameter itself is prefixed by an * (asterisk).
Note: To pass a pointer to an object, prefix the parameter in the CALL statement with &. To receive a pointer to an object, prefix the parameter's declaration with *. In the latter case, this may mean adding a second * to a parameter that already has an *. For example, to receive a pointer by value, declare it as follows:
int *ptr;
But to receive the same pointer by reference, declare it as the following:
int **ptr;
The default for arrays is to pass by reference.
Near reference is the default for passing pointers in small and medium model C. Far reference is the default for the compact, large, and huge models.
Note All C programs that are linked with COBOL must be compiled with the large memory model.
The COBOL to C interface does not support near heap in the C run time. This means you should not use the function calls that access near heap in your C programs. This includes the following functions:
To work around this, compile and link with C as the initial program. After the main C program begins, the COBOL routine can be CALLed. The COBOL code can then CALL back and forth with C. Since the C support modules are not used, there are no special restrictions on the near heap functions.
C stores strings as simple arrays of bytes (like COBOL) but also uses a null character [ASCII NULL (0)] as the delimiter to show the end of the string. For example, consider the string declared as follows:
char str[] = "String of text"
The string is stored in 15 bytes of memory as follows:
|S|t|r|i|n|g| |o|f| |t|e|x|t|\0|
When passing a string from COBOL to C, the string will normally not have a NULL appended to the end. Because of this, none of the C routines that deal directly with a string (printf, sprintf, scanf, and so on) can be used with these strings unless a NULL is appended to the end.
A NULL can be put at the end of a COBOL string by using the following declaration:
01 CSTRING.
05 THE_STRING PIC X(10).
05 FILLER PIC X VALUE x"0".
Several compile and link options need to be used when interfacing C and COBOL. The standard C compile line is as follows:
CL /c /Aulf CProgName ;
Option | Description |
/c | Compiles without linking (produces only an .OBJ file). |
/Aulf | Sets up a customized large memory model. |
u | SS not equal to DS. DS is reloaded on function entry. |
l | Selects large memory model Far (32-bit) code pointers. |
f | Selects large memory model Far (32-bit) data pointers. |
The standard LINK line for COBOL CALLing C is as follows:
For MS-DOS®
LINK CobProg CProg MFC6INTF C6DOSIF C6DOSLB,,,LCOBOL COBAPI
LLIBCE/NOE/NOD;
For OS/2®
LINK CobProg CProg MFC6INTF C6OS2IF C6OS2LB,,,LCOBOL OS2
LLIBCEP/NOE/NOD;
The standard LINK line for C CALLing COBOL is as follows:
For DOS
LINK CProg CobProg,,,LLIBCE LCOBOL COBAPI/NOE/NOD;
For OS/2
LINK CProg CobProg,,,LLIBCEP LCOBOL OS2/NOE/NOD;
Note that the order in which the libraries are specified in the LINK line is important.
Microsoft® COBOL versions 4.0 and 4.5 introduced the shared run-time system. Although it is generally more useful to link your applications using the static run-time system (LCOBOL.LIB), you may also choose to link the applications with the shared run-time library (COBLIB.LIB) to take advantage of its more efficient methods of utilizing memory. In order to do this and link your applications to Microsoft C, you must SET the COBPOOL environment variable as referenced in the Microsoft COBOL Operating Guide.
This list supplies a simple checklist to go over when you encounter problems doing mixed-language programming:
The following batch files can be helpful when using the sample programs below. The CBC6.BAT file can be used to set your environment table correctly, but think of it as a convenience rather than a necessity when using. This means that you should already have these parameters preset in your environment when using both languages in tandem.
CBC6.BAT
REM THIS BATCH FILE SHOULD CONFIGURE THE ENVIRONMENT TABLE TO ENABLE
REM YOU TO COMPILE BOTH THE C AND COBOL APPLICATIONS UNDER MS-DOS
REM CORRECTLY.
REM
REM PLEASE LOOK CLOSELY AT THE ENVIRONMENT SETTINGS AND CHANGE THOSE
REM NECESSARY IN YOUR OWN AUTOEXEC.BAT FILE.
REM
REM NOTE: IF, AFTER INVOKING THIS BATCH FILE, YOU SEE THE MESSAGE
REM "OUT OF ENVIRONMENT", YOU WILL HAVE TO INCREASE THE AMOUNT OF
REM ENVIRONMENT TABLE SPACE. PLEASE SEE YOUR MS-DOS MANUAL UNDER THE
REM HEADING COMMAND.COM FOR INSTRUCTIONS ON HOW TO DO THIS.
REM
SET LIB=C:\COBOL\LIB;C:\C600\LIB
SET INCLUDE=C:\C600\INCLUDE;C:\COBOL\SOURCE
SET HELPFILES=C:\C600\HELP\*.HLP
SET INIT=C:\C600\INIT;C:\COBOL\INIT
PATH=C:\COBOL\BINB;C:\COBOL\BINR;C:\C600\BINB;C:\C600\BIN;C:\DOS
RUN.BAT
REM THIS BATCH FILE CAN BE USED TO COMPILE AND LINK BOTH THE C AND
REM COBOL APPLICATIONS FOR MS-DOS.
REM
REM THOSE PROGRAMS THAT REQUIRE A DIFFERENT METHOD OF COMPILING AND
REM LINKING WITHIN THE SCOPE OF THIS APPLICATION NOTE WILL BE NOTED.
REM
REM TO INVOKE THIS BATCH FILE, YOU MUST ENTER THE BATCH FILE NAME,
REM FOLLOWED BY THE C PROGRAM NAME (WITH NO EXTENSION), FOLLOWED BY
REM THE COBOL PROGRAM NAME. FOR EXAMPLE:
REM
REM RUN <C PROGRAM NAME> <COBOL PROGRAM NAME>
REM
cl /c /Aulf %1.c
COBOL %2;
LINK %2 %1 MFC6INTF C6DOSIF C6DOSLB,,,LCOBOL COBAPI LLIBCER /NOD/NOE;
RUN_C.BAT
REM THIS BATCH FILE CAN BE USED TO COMPILE AND LINK UNDER MS-DOS
REM ONLY WHEN THE SAMPLE C CODE IS CALLING A COBOL PROCEDURE.
REM
REM THOSE PROGRAMS THAT REQUIRE A DIFFERENT METHOD OF COMPILING AND
REM LINKING WITHIN THE SCOPE OF THIS APPLICATION NOTE WILL BE NOTED.
REM
REM TO INVOKE THIS BATCH FILE, YOU MUST ENTER THE BATCH FILE NAME,
REM FOLLOWED BY THE C PROGRAM NAME (WITH NO EXTENSION), FOLLOWED BY
REM THE COBOL PROGRAM NAME. FOR EXAMPLE:
REM
REM RUN <C PROGRAM NAME> <COBOL PROGRAM NAME>
REM
cl /c /Aulf %1.c
COBOL %2;
LINK %1 %2,,,LLIBCER LCOBOL COBAPI/NOD/NOE;
WINRUN.BAT
REM THIS BATCH FILE IS USED TO COMPILE AND LINK THE QUICKWIN
REM APPLICATION PROGRAM DEMONSTRATED IN THIS DOCUMENT. THIS IS A
REM SPECIALIZED BATCH FILE. IT HAS BEEN CREATED SPECIFICALLY FOR THE
REM SAMPLE PROGRAM PRESENTED.
REM TO CREATE A GENERIC BATCH FILE, CHANGE ALL OCCURRENCES CDLL AND
REM TEST TO %1 AND %2 RESPECTIVELY.
REM
cl /ML /Gs /c /Zi CDLL.C
LINK CDLL+LIBENTRY,CDLL.DLL,CDLL.MAP/MAP,LDLLCEW+LIBW/NOE/NOD,CDLL
/CO;
IMPLIB CDLL.LIB CDLL.DLL
COPY CDLL.DLL C:\
COBOL TEST TARGET(286);
LINK CBLWINC+TEST+ADIS+ADISINIT+ADISKEY,TEST.EXE,,LIBW+LLIBCEW+LCOBOL+
COBAPIDW+CDLL.LIB,TEST.DEF/NOE/NOD;
The following sample code demonstrates how to pass common numeric types to a C routine by reference and by value.
COBNUMS.CBL
* Passing Common Numeric Types to C by Reference and by Value
working-storage section.
01 field1 pic 9(4) comp-5 value 123.
01 field2 pic 9(8) comp-5 value 123456.
01 field3 pic 9(4) comp-5 value 456.
01 field4 pic 9(8) comp-5 value 456789.
procedure division.
* Fields 1 and 2 (below) are passed BY REFERENCE. The keywords
* are omitted here since BY REFERENCE is the default method.
call "_CFUNC" using field1, field2,
by value field3,
by value field4.
display "Returned pic 9(4): " field1.
display "Returned pic 9(8): " field2.
stop run.
CFUNC.C
#include <stdio.h>
void CFunc(int *RefInt, long *RefLong, int ValInt, long ValLong)
{
printf("By Reference: %i %li\r\n", *RefInt, *RefLong);
printf("By Value : %i %li\r\n", ValInt, ValLong);
*RefInt = 321;
*RefLong = 987654;
}
OUTPUT
Returned PIC 9(4): 00321
Returned PIC 9(8): 000987654
By Reference: 123 123456
By Value : 456 456789
The following sample code demonstrates how to pass an alphanumeric string from C to COBOL.
_COBPROG.CBL
program-id. "_cobprog".
data division.
linkage section.
01 field1 pic x(6).
procedure division using field1.
display "String from C: " field1.
stop run.
C.C
#include <stdio.h>
extern cdecl cobprog(char *Cptr);
char Cptr[] = "ABCDEF";
void main() {
cobprog(Cptr);
}
Output
String from C: ABCDEF
The following sample code demonstrates how to pass a record from COBOL to a C data struct.
STRUCT.CBL
$set vsc2 rtncode-size(4)
* Passing a Record from COBOL to a C struct
data division.
working-storage section.
01 rec-1.
02 var1 pic X(8) value "HELLO".
02 var2 pic X(12) value "W O R LD".
02 varc2 pic 9(04) comp-5 value 2.
02 varc3 pic 9(04) comp-5 value 3.
02 varc4 pic 9(04) comp-5 value 4.
02 varc5 pic 9(04) comp-5 value 5.
02 varc1 pic 9(04) comp-5 value 1.
procedure division.
call "C_FUNCTION1" using by reference rec-1.
display "CBL varC--> " varC1.
display "CBL varC--> " varC2.
display "CBL varC--> " varC3.
display "CBL varC--> " varC4.
display "CBL varC--> " varC5.
display "CBL var1--> " var1.
display "CBL var2--> " var2.
stop run.
STRUCTC.C
#include <stdio.h>
struct struct1 {
unsigned char var1[8];
unsigned char var2[12];
unsigned int var3[5];
};
function1(struct struct1 far *p1)
{
int a;
for (a=0; a<5; a++)
printf("%i\n",p1->var3[a]);
for (a=0; a<8; a++)
printf("%c", p1->var1[a]);
printf("\n");
for (a=0; a<12; a++)
printf("%c", p1->var2[a]);
printf("\n");
}
OUTPUT
2
3
4
5
1
HELLO
W O R LD
CBL VARC--> 00001
CBL VARC--> 00002
CBL VARC--> 00003
CBL VARC--> 00004
CBL VARC--> 00005
CBL VAR1--> HELLO
CBL VAR2--> W O R LD
The following sample code demonstrates how to pass a record from struct from C to COBOL.
_COBPROC.CBL
identification division.
environment division.
data division.
working-storage section.
01 Integer pic 9(4).
01 Long pic 9(8).
linkage section.
01 CobRec.
03 COBInt pic s9(4) comp-5 value zero.
03 COBLong pic s9(8) comp-5 value zero.
03 COBString pic x(21) value spaces.
procedure division using CobRec.
move COBInt to Integer.
move COBLong to Long.
display "Integer from C: " Integer.
display "Long integer from C: " Long.
display "String from C: " COBString.
exit program.
STRUCT2C.C
#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
struct CobRec // defines data type CobRec
{
unsigned int varInt; // integer variable
unsigned long varLong; // long int
char szString[21]; // string variable
};
/* COBOL routines are cdecl; this means the name must be prefixed
* with '_'. Alternatively, you can manually reverse the
* parameters.
*/
extern far cdecl COBPROC(struct CobRec *cPtr);
main()
{
struct CobRec *cPtr; // declare pointer to struct
// get memory to hold struct
cPtr = (struct CobRec *) _fmalloc(sizeof(struct CobRec));
/* NOTE: COBOL will be the main program unless BP is nonzero.
* BP is zero until some local variables are allocated and used.
*
* In this example, we do use some local variables; therefore, this
* is taken care of already.
*/
printf("Positive Integers and String\n");
cPtr->varInt = 32767; // refer to member of struct and
cPtr->varLong = 60000; // assign values
strcpy(cPtr->szString,"This is a test string");
COBPROC ( cPtr); // CALL to COBOL procedure
printf("\n\n\n");
printf("Negative Integers and String\n");
cPtr->varInt = -32765;
cPtr->varLong = -987654;
strcpy(cPtr->szString,"Here's another string\n");
COBPROC ( cPtr);
}
OUTPUT
Positive Integers and String
Integer from C: 2767
Long integer from C: 00060000
String from C: This is a test string
Negative Integers and String
Integer from C: 2765
Long integer from C: 00987654
String from C: Here's another string
The following sample code demonstrates how to pass an array of integers from COBOL to C.
INTARRAY.CBL
* Passing an Array of Integers from COBOL to C
data division.
working-storage section.
01 t-count pic 99.
01 t-table.
05 the-table pic 9(4) comp-5 occurs 5 times.
procedure division.
perform varying t-count from 1 by 1 until t-count > 5
move t-count to the-table(t-count)
end-perform.
call "C_CProc" using t-table.
stop run.
CNTARRAY.C
#include <stdio.h>
void CProc(int IntTable[4]) {
int count;
for (count = 0; count < 5; count++)
printf("Array [%i]: %i\r\n", count, IntTable[count]);
}
OUTPUT
Array [0]: 1
Array [1]: 2
Array [2]: 3
Array [3]: 4
Array [4]: 5
The following sample code demonstrates how to pass a two-dimensional array of long integers from COBOL to C.
LINT.CBL
* Passing long integers from COBOL to C
$set bound
data division.
working-storage section.
01 I1 pic 9.
01 J1 pic 9.
01 t-table.
02 t-field occurs 2 times.
05 the-table pic 9(8) comp-5 occurs 3 times.
procedure division.
perform varying I1 from 1 by 1 until I1 > 2
perform varying J1 from 1 by 1 until J1 > 3
move J1 to the-table(I1, J1)
end-perform
end-perform.
call "_CProc" using t-table.
stop run.
LINTC.C
#include <stdio.h>
void CProc(long IntTable[2][3]) {
int i, j;
for (i = 0; i < 2; i++)
for (j = 0; j < 3; j++)
printf("Array [%i,%i]: %ld\r\n", i, j, IntTable[i][j]);
}
OUTPUT
Array [0,0]: 1
Array [0,1]: 2
Array [0,2]: 3
Array [1,0]: 1
Array [1,1]: 2
Array [1,2]: 3
The following sample code demonstrates how to pass a two-dimensional array of records from C to COBOL
_COBPROC.CBL
program-id. "_CobProc".
data division.
working-storage section.
01 I1 pic 9.
01 J1 pic 9.
linkage section.
01 the-table.
02 t-table occurs 2 times.
05 t-field occurs 3 times.
10 field1 pic 9(4) comp-5.
10 field2 pic x(6).
procedure division using the-table.
perform varying I1 from 1 by 1 until I1 > 2
perform varying J1 from 1 by 1 until J1 > 3
display "table[" I1 "][" J1 "]: " field1(I1, J1)
display " " field2(I1, J1)
end-perform
end-perform.
stop run.
2DRECS.C
#include <stdio.h>
struct TableStruc { /* define structure */
int TheInt;
char String[6];
} TheTable[2][3];
extern void far cdecl CobProc(struct TableStruc TheTable[2][3]);
void main() {
int i, j;
for (i = 0; i < 2; i++) /* initialize structure */
for (j = 0; j < 3; j++) {
TheTable[i][j].TheInt = j;
Sprintf(TheTable[i][j].String, "[%1i][%1i]", i, j);
}
CobProc(TheTable); /* CALL COBOL routine */
}
OUTPUT
table[1][1]: 00000
[0][0]
table[1][2]: 00001
[0][1]
table[1][3]: 00002
[0][2]
table[2][1]: 00000
[1][0]
table[2][2]: 00001
[1][1]
table[2][3]: 00002
[1][2]
The following sample code demonstrates how to pass integers by reference from COBOL to C.
COBINT.CBL
* Passing integers by reference from COBOL to C
working-storage section.
01 passvar1 pic 9(4) comp-5 value 16384.
01 passvar2 pic 9(4) comp-5 value 33.
procedure division.
display "Before the call to the C swapping function...".
display "Passvar1 is equal to: " passvar1.
display "Passvar2 is equal to: " passvar2.
call "_SwapFunc" using by reference passvar1
by reference passvar2.
display "After the call to the C swapping function...".
display "Passvar1 is equal to: " passvar1.
display "Passvar2 is equal to: " passvar2.
stop run.
CINT.C
/* Manipulates integers passed from a COBOL program */
#include <stdio.h>
void SwapFunc(int *var1, int *var2)
{
int tmp; /* Temporary value for use in swap */
tmp = *var1;
*var1 = *var2;
*var2 = tmp;
return;
}
OUTPUT
Before the call to the C swapping function...
PassVar1 is equal to: 16384
PassVar2 is equal to: 00033
After the call to the C swapping function...
PassVar1 is equal to: 00033
PassVar2 is equal to: 16384
The following sample code demonstrates how to pass an integer from COBOL to C.
CBLINT.CBL
working-storage section.
01 pass-var pic 9(4) comp-5 value 3.
procedure division.
call "_Circum" using by value pass-var.
display "Radius of circle: " pass-var.
display "Circumference of circle: " return-code.
stop run.
C.C
#include <stdio.h>
int Circum(int Radius) {
float cir;
cir = 3.14159 * Radius * Radius;
return((int) cir);
}
OUTPUT
Radius of circle: 00003
Circumference of circle: +0028
The following sample code demonstrates passing a long integer from COBOL to C.
LINT.CBL
$set rtncode-size(2)
working-storage section.
01 pass-var pic 9(4) comp-5.
procedure division.
display "Radius of circle?".
accept pass-var.
call "_Area" using by value pass-var.
display "Area of circle: " return-code.
stop run.
LINTC.C
#include <stdio.h>
long Area(int Radius) {
float cir;
cir = 3.14159 * Radius * Radius;
return((long) cir);
}
OUTPUT
Radius of circle?
1
Area of circle: +0003
The following sample code demonstrates how to pass a string from COBOL to C.
COBSTR.CBL
* Passing a string from COBOL to C
identification division.
program-id. cobstr.
data division.
working-storage section.
01 passvar pic x(15) value "Replace this".
procedure division.
display "This is what is passed: " passvar.
call "_Funct" using pass-var.
display "This is what comes back: " passvar.
stop run.
CSTR.C
#include <ctype.h>
void * Funct(char *Rvalue)
{
char *cp;
cp = Rvalue;
while (*cp != '\0') {
*cp = toupper(*cp);
++cp;
}
return;
}
OUTPUT
This is what is passed: Replace this
This is what comes back: REPLACE THIS
The following samples demonstrate how to call a C 6.x routine from a COBOL 4.5 program, where the C function, in turn, spawns another COBOL 4.5 executable.
Note: The COBOL program titled COB2.CBL must be compiled and linked as a stand-alone executable module. Use the following lines to compile and link this program:
COBOL COB2;
LINK COB2,,,LCOBOL COBAPI/NOE/NOD;
MAIN.CBL
* How to call a C function that executes another COBOL
* program.
program-id. main.
working-storage section.
01 commandL.
05 filler pic x(01) value 'S'.
05 cmdlin pic x(124) value "COB2.EXE".
procedure division.
display "In COBOL program 1".
call "_pcexec" using commandL.
display "End of COBOL program 1".
stop run.
PCEXEC.C
#include <stdio.h>
#include <process.h>
pcexec (commandL)
char far commandL[125];
{
printf ("Prior to C call of COB2.EXE \n");
spawnl (P_WAIT, "COB2.EXE", "COB2", "spawnl", NULL);
printf("After C call to COB2.EXE \n");
}
COB2.CBL
* This program must be a separate executable.
program-id. cob2.
procedure division.
display "Inside COBOL program 2".
stop run.
OUTPUT
In COBOL program 1
Prior to C call of COB2.EXE
Inside COBOL program 2
After C call to COB2.EXE
End of COBOL program 1
The following samples demonstrate how a COBOL 4.5 Quickwin application can call a Windows™-based DLL written in C 6.x.
MAIN.CBL
working-storage section.
77 Var1 pic 9(4) comp-5.
77 Char pic x.
procedure division.
move 1 to Var1.
display "Prior to DLL call: " at 0101
display Var1 at 0120.
call 'cdll' using by reference Var1.
display "After DLL call: " at 0201.
display Var1 at 0217.
call "cbl_read_kbd_char" using Char.
stop run.
MAIN.DEF
EXETYPE WINDOWS 3.0
APPLOADER '__MSLANGLOAD'
PROTMODE
STUB 'WINSTUB.EXE'
CODE PRELOAD FIXED
DATA PRELOAD FIXED MULTIPLE
STACKSIZE 16384
HEAPSIZE 1024
EXPORTS WNDPROC
CDLL.C
#include <windows.h>
int FAR PASCAL LibMain(HANDLE hInstance,
WORD wDataSeg,
WORD cbHeapSize,
LPSTR lpszCmdLine)
{
//Additional DLL initialization fits here
if (cbHeapSize != 0)
UnlockData(0);
return (1);
}
VOID FAR PASCAL cdll(int _far *piIntPointer)
{
if((*piIntPointer >= -32768) && (*piIntPointer < 32767))
{
(*piIntPointer)++;
return(1);
}
else
{
return(0);
}
}
VOID FAR PASCAL WEP (int nParameter)
{
if (nParameter == WEP_SYSTEM_EXIT)
{
return (1);
}
else
{ if (nParameter == WEP_FREE_DLL)
{
return (1);
}
else {
return (1);
}
}
}
CDLL.DEF
LIBRARY cdll
DESCRIPTION 'C DLL FOR WINDOWS 3.0'
EXETYPE WINDOWS
STUB 'WINSTUB.EXE'
CODE PRELOAD MOVEABLE DISCARDABLE
DATA PRELOAD MOVEABLE SINGLE
HEAPSIZE 0
EXPORTS Cdll @1
WEP @2 RESIDENTNAME