DevC++ C calling OpenCobolIDE module

670 views Asked by At

I'm trying to call an OpenCobol file handling routine from a program written in C using the Dev C++ IDE (they're free and I like them).

The Cobol environment generates a DLL when you compile the code as a module so I was hoping that I could just use the normal methods of calling the code (see below). If compiled and run as below I get the error message "libcob: cob_init() has not been called", however, after including the header and the library in the relevant places and uncommenting the sections in the code I'm getting an "undefined reference to _imp_cob_init" message.

I'm obviously missing something simple. Any help would be appreciated.

AdyB

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <windows.h>

/*
#ifdef __cplusplus
extern "C" {
#endif
#include "libcob.h"

#ifdef __cplusplus
}
#endif
*/

typedef void (*pFile)(char*);

pFile filehandler=NULL;
char dllpath[256];

int main(int argc, char *argv[])
{
    HINSTANCE hLib;
    char txt[100]="this is a test";

/*  cob_init(0, NULL);*/

    hLib = LoadLibrary("F:\\source\\filehandler\\bin\\filehandler.dll");
    if (!hLib)
    {
      perror("Error loading dll");
      exit(1);
    }

    GetModuleFileName((HMODULE)hLib, (LPTSTR)dllpath, sizeof(dllpath));
    printf("Opened %s\n\n", dllpath);

    filehandler = (pFile)GetProcAddress((HMODULE)hLib, "filehandler");
    if (!filehandler)
    {
      perror("Can't find dll function");
      exit(1);
    }

    filehandler(txt);

    return 0;
}
1

There are 1 answers

0
Simon Sobisch On

You've mixed the calls and now have partially use of the COBOL runtime library libcob and partially not using it. Normally you do one of the following (most times the third option):

  1. just use the generated COBOL dll as "normal" dll and don't care about any cleanup

  2. use a "clean" runtime initialization and cleanup

  3. use option and leave loading of COBOL modules to libcob

Option 1: "simplest solution" - use COBOL module, no cleanup

Remove the comments for libcob in your C source.

Compile your COBOL module with cobc -fimplicit-init filehandler.cob, this way the initialization is done automatically.

Note: This leads to a minimal additional startup time for the module load (how much depends on the version of libcob in use, but it isn't much in general).

Possible issue: You don't have an option to cleanup the COBOL parts, they will be still active after you finished (and even closed) the module handle.

Option 2: "cleaner solution but still involving system specific parts for module load"

Compile your COBOL module without -fimplicit-init and change your code to

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <windows.h>

#ifdef __cplusplus
extern "C" {
#endif
#include "libcob.h"

#ifdef __cplusplus
}
#endif

typedef void (*pFile)(char*);

pFile filehandler=NULL;
char dllpath[256];

int main(int argc, char *argv[])
{
    HINSTANCE hLib;
    char txt[100]="this is a test";

#if !defined PASS_COMMAND_LINE_TO_COBOL
    cob_init(0, NULL);     /* initialization of COBOL runtime, no command line passed */
#else
    cob_init(argc, argv);  /* initialization of COBOL runtime, complete command line passed */
#endif

    hLib = LoadLibrary("F:\\source\\filehandler\\bin\\filehandler.dll");
    if (!hLib)
    {
      perror("Error loading dll");
      exit(1);
    }

    GetModuleFileName((HMODULE)hLib, (LPTSTR)dllpath, sizeof(dllpath));
    printf("Opened %s\n\n", dllpath);

    filehandler = (pFile)GetProcAddress((HMODULE)hLib, "filehandler");
    if (!filehandler)
    {
      perror("Can't find dll function");
      exit(1);
    }

    filehandler(txt);

#if defined (cob_c8_ptr) /* hack for checking if you use a 2.x+ version, not *really* needed in this case */
    cob_tidy;  /* finalizing of COBOL runtime (OC/GC function) */
#else
    cobtidy;   /* finalizing of COBOL runtime (MF compatibility function name available since OpenCOBOL 1.1 released Feb 2009 [still available (as define) in 2.x+ version]) */
#endif

    return 0;
}

or (if you really want to exit after the COBOL call) changing the last code part to:

    int cob_return_int = filehandler(txt);
    cob_stop_run (cob_return_int);

Important: You now need both including libcob.h for the C compiler to know the function declarations and link to the libcob library (most likely by adding -lcob to compiler options in DevC++ (otherwise you'll get the "undefined reference" message from the C linker).

Option 3: "clean solution leaving everything COBOL to libcob"

Obviously you have to link against libcob in this case, too.

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>

#ifdef __cplusplus
extern "C" {
#endif
#include "libcob.h"

#ifdef __cplusplus
}
#endif

int main(int argc, char *argv[])
{
    cob_call_union  filehandler_module;
    int     cob_return_int;
    char txt[100]="this is a test";

    /* initialization of COBOL runtime, complete command line passed */
    cob_init(argc, argv);

    /* search and load a function pointer for program-id "filehandler" in a module called "filehandler" */
    filehandler_module.funcvoid = cob_resolve ("filehandler");
    if (filehandler_module.funcvoid == NULL) {
        /* this will display the error and return with 1, as your old code did */
        cob_call_error ();
        /* if you just want the error text in a local buffer call `cob_resolve_error` instead */
    }

    /* calling the COBOL module "filehandler" and store its return-code in cob_return_int */
    cob_return_int = filehandler_module.funcint(txt);

    /* NOTE: you may call additional COBOL modules (or the same with a different option) here, maybe depending on `cob_return_int` */

    cob_tidy;  /* finalizing the COBOL runtime */

    /* NOTE: more code goes here, likely handling `cob_return_int` */

    return 0;
}

The only thing that is "missing" in the code is the path to "filehandler.dll". libcob tries the current path first, so if your module is there you don't need to tweak it.

Specify the module lookup path via set COB_LIBRARY_PATH=F:\source\filehandler\bin outside of your application.

If you really want to hardwire it in your application and have a very recent version (read as "nearly current development snapshot") you can do so by calling cob_setenv("COB_LIBRARY_PATH", "F:\\source\\filehandler\\bin", 1); - doing so with setenv() or putenv() may work (but this is highly dependent on the C runtime library used for your program and for libcob.

If possible I'd allways suggest (for a "current" version of GnuCOBOL) to use a runtime configuration file and specify its path with set COB_RUNTIME_CONFIG=X:\PATH\TO\filehandler.cfg or the environment functions outlined above.

And I'd always suggest to switch to a recent version of GnuCOBOL if you use anything older than GnuCOBOL 2.2rc, but this is a different issue...