The "Sub" Interface

Every time a CALL statement executes, it calls a C routine called "sub". This routine is passed the name of the called program and its USING parameters. You may modify this routine to recognize the call name that you want to assign to a C subprogram and perform the appropriate code. This routine is contained in the "sub.c" file.

The "sub" routine is passed two arguments: argc and argv. The argv parameter is an array of character pointers. The argc parameter is an integer count of the number of elements in the argv array. The first element in argv points to the call name exactly as it appears in the COBOL CALL statement. This name is terminated with a NULL character. The remaining elements of argv point to each of the USING arguments.

The "sub" function should check to see if the called name is one that should be handled in C. It can do this by comparing "argv[0]" with the desired routine name using the "strcmp" C library routine. If the routine is one that is not handled by a C subroutine, then "sub" should return a negative value. This indicates to runcbl that the CALL statement has not been fulfilled and that it should try to find a COBOL subprogram by that name. If the routine is handled by the "sub" function, then a zero should be returned. In this case, runcbl assumes that the CALL statement has been completed and it continues with the next statement. Finally, if "sub" returns a positive value, then runcbl executes a STOP RUN, returning the value to the operating system as runcbl's exit value. See "sub.c" for an example of this interface.

When processing a USING parameter, note that the C subroutine must know what internal format the parameter uses. Also note, that in COBOL, literal values are not terminated by a NULL character. Thus, you should not treat a passed value as a C string unless the calling program ensures that the passed value is NULL terminated. This can be accomplished in the following fashion:

STRING   "literal", LOW-VALUES, DELIMITED BY SIZE
         INTO ITEM-1
CALL NAME-1 USING ITEM-1.
Note:

The "sub" interface provides compatibility with the RM/COBOL-85 interface. At run time "sub" performs a linear search for a called routine. This can be inefficient if your program calls a large number of C routines. We recommend that you use the "sub85" or "direct" interface.

Placing the "Sub" Routine in a DLL

In addition to linking the "sub" function directly into the runtime, Windows users may place the "sub" routine into one or more DLL files.

You must specify which routine to use as the "sub" interface routine by setting the DLL_SUB_INTERFACE configuration variable. Then you call the DLL from your COBOL program. The runtime loads the DLL, then checks DLL_SUB_INTERFACE for the name of the routine to use as the "sub" interface routine. For example, the following C program (subdll.c) is the source for the DLL that contains the "sub" interface, called AcuSub in this example:

#include <stdio.h>
#include <windows.h>
#include "sub.h"

#define   DllExport             __declspec(dllexport)
#define   CCallingConvention    __cdecl

DllExport int CcallingConvention
AcuSub( int argc, char *argv[] )
{

    if ( strcmp( argv[0], "MSGBOX" ) == 0 ) {
             MessageBox( NULL, argv[1], NULL, MB_OK );
             return Okay;
    }

    return NotFound;

}   /* AcuSub */

/* end of subdll.c */

The following COBOL program ("callsub.cbl") shows how the DLL is loaded and called:

program-id.  test.
working-storage section.

77  message-text          pic x(80).

procedure division.
main-logic.
    display standard window.

* Load DLL and establish "sub" interface

    set environment "dll-sub-interface" to "AcuSub".
    call "subdll".

* Call "MSGBOX", one of the routines handled by "AcuSub" 

    move "This is a test message" to message-text.
    inspect message-text replacing trailing spaces by 
        low-values.
    call "msgbox" using message-text.

    accept omitted.
    stop run.

If DLL_SUB_INTERFACE is blank when the DLL is loaded, no "sub" routine is used in that DLL.

When a CALL statement executes, the "sub" interface routine in each loaded DLL is called, in the order that they were loaded, until one of them returns that it has executed the subroutine. If none of the DLLs returns success, the normal, or linked-in, "sub" routine is called. If that does not return success, then the standard calling sequence resumes. As soon as any routine returns success, the CALL is considered satisfied and no further processing of the CALL is done.

If you CANCEL a DLL with an active "sub" interface, that interface is removed from the list of available interfaces and the DLL is unloaded.