Search code examples
c#dllwindowcobol

Call Cobol or C functions in Cobol program called by C# program


I work on a simple console application in C# 4.6.1. I can make cobol program using Micro Focus environnent. My goal is to call, from console application, cobol program which have calls to another cobol program.

Today I can call with success cobol methods in cobol dll file, using DllImport or LoadLibrary.

In my example, my cobol method named Hello, in dll file hello.dll, print "Hello :D" and in the same way, my another cobol program named Itsme, in dll file itsme.dll, print "It's me".

Inside Cobol program, we can call another program with his name. In my example, I add the the following line in my Hello program :

CALL "ITSME"

In my console application, I load both dll file (hello.dll and itsme.dll). When I call my method Hello, it's supposed to print "Hello It's me". But it won't, because of :

Load error : file 'Itsme'

error code: 173, pc=0, call=1, seg=0 173
Called program file not found in drive/directory

I know it work on Unix system, using dlopen instead of LoadLibrary. LoadLibrary isn't the equivalent of dlopen ?

EDIT : There is a little sample of code after looking to Simon Sobisch answer :

#include "stdafx.h"
#include "stdio.h"
#include "windows.h"
#include "cobtypes.h"

typedef int(__stdcall *f_cobinit)();
typedef cobrtncode_t(__stdcall *f_cobcall)(const cobchar_t *callname, int argcnt, cobchar_t **argvec);

int main()
{
    HINSTANCE dll1 = LoadLibrary("CobolDlls.dll");
    HINSTANCE dll2 = LoadLibrary("CobolDlls2.dll");
    HINSTANCE dllCbl = LoadLibrary("cblrtsm.dll");

    printf("cobinit : ");
    f_cobinit init = (f_cobinit)GetProcAddress(dllCbl, "cobinit");
    printf("%d", init());

    f_cobcall hi = (f_cobcall)GetProcAddress(dllCbl, "cobcall");
    cobrtncode_t c = hi((cobchar_t*)"P2", 0, NULL);
    
    getchar();
}

I successfully load librairies, and my call to cobinit is working (result : 0). But when I call cobcall method, I have the error code 173 seen previously.

I precise that "P2" is refering to a cobol program named "P2" included in "CobolDlls.dll", which is build as a shared single dynmic library with MicroFocus, and all Dlls are in the same directory than the console application.

P2 program :

   identification division.
   program-id. P2.
   data division.
   working-storage section.
   local-storage section.
   procedure division.
   display "Hello I'm P2"
   goback.

Thanks for your help, Yoann


Solution

  • Note: This is a guess as I did not tested it, but for most COBOL implementations there is a a runtime library (which should be the target for dlopen / LoadLibrary) which provides functions to use it.

    A quick search in the Micro Focus documentation showed C Functions for Calling COBOL, you likely want to use cobinit(), cobcall() and cobtidy().

    As everything concerning the COBOL parts is done by the runtime library (which needs to be either linked or loaded itself) you'll need to set options like where to find the modules and other possibly relevant settings before the call to cobinit().

    One setting possibly needed: COBPATH - If the COBOL modules do not reside in the process' current directory - set the same way you'd set PATH variable (entries separated by ";" on Windows)

    I'd try something like the following in this case (in C#, the sample was C++)

    System.Environment.SetEnvironmentVariable("COBPATH", "X:\path;Y:\other-path");
    

    According to the documentation about Calling Programs combined programs are loaded by loading the library containing them first with a plain CALL. Your adjusted program would then look like:

    #include "stdafx.h"
    #include "stdio.h"
    #include "windows.h"
    #include "cobtypes.h"
    
    typedef int(__stdcall *f_cobinit)();
    typedef cobrtncode_t(__stdcall *f_cobcall)(const cobchar_t *callname, int argcnt, cobchar_t **argvec);
    
    int main()
    {
        HINSTANCE dllCbl = NULL;
    
        f_cobinit cobInit = NULL;
        f_cobcall cobCall = NULL;
    
        int retCode;
    
        printf("load COBOL runtime : ");
        HINSTANCE dllCbl = LoadLibrary("cblrtsm.dll");
        if (!dllCbl) {
            retCode = (int)GetLastError();
            printf("failure: %d\n", retCode);
            return retCode;
        }
        puts("success");
    
        printf("lookup COBOL functions : ");
        cobInit = (f_cobinit)GetProcAddress(dllCbl, "cobinit");
        cobCall = (f_cobcall)GetProcAddress(dllCbl, "cobcall");
        printf("init: %p, call: %p - ", (void *)cobInit, (void *)cobCall);
        if (!cobInit || !cobCall) {
            retCode = (int)GetLastError();
            printf("failure: %d\n", retCode);
            return retCode;
        }
        puts("success");
    
    
        printf("cobinit : ");
        retCode = cobInit();
        if (retCode) {
            printf("failure: %d\n", retCode);
            return retCode;
        }
        puts("success");
    
        printf("preload CobolDlls : ");
        retCode = cobCall((cobchar_t*)"CobolDlls", 0, NULL);
        puts("success");
    
        //printf("preload CobolDlls2 : ");
        //retCode = cobCall((cobchar_t*)"CobolDlls2", 0, NULL);
        //puts("success");
    
        printf("do actual CALL "P2" : ");
        retCode = cobCall((cobchar_t*)"P2", 0, NULL);
        printf("returned with: %d\n", retCode);
    
        getchar();
    }
    

    Note: Be aware that the COBOL runtime may issue an exit (especially when there are errors in the COBOL parts or a module does STOP RUN). You want to make sure to be able to catch it.