Welcome Serena Central users! CLICK HERE
The migration of the Serena Central community is currently underway. Be sure to read THIS MESSAGE to get your new login set up to access your account.
mfisher Absent Member.
Absent Member.
4688 views

Calling C++ functions from COBOL

Jump to solution

I see in the ACUCOBOL-GT docs that it is possible to call C/C++ functions from COBOL using the CALL statement.

In Windows, I've managed to do this using pure C code that was compiled with gcc. This was done using the version of gcc that ships with MinGW in release 5.1.1 of the Qt framework.

If I try to call functions in a DLL that was compiled with g++ or cl.exe (the Visual C++ compiler), I keep hitting the ON EXCEPTION part of my CALL statement. Obviously I'm missing something.

I'm aware of the name-mangling issue with C++ compilers, and have tried to work around it by using __declspec(dllimport) and __cdecl in the C++ function declarations. However, this does not resolve the problem.

Can anyone provide a simple example of both C++ code and compile/link commands, which would allow C++ library code to be used from ACUCOBOL? And can this only be done using COM DLLs? (That's the one thing I haven't tried, because I don't have a development environment that supports creating COM DLLs.)

I'm interested in doing this both in Windows and Linux, with the current release of ACUCOBOL-GT.

Thank you in advance for any assistance!

0 Likes
1 Solution

Accepted Solutions
Micro Focus Contributor
Micro Focus Contributor

RE: Calling C++ functions from COBOL

Jump to solution

Got it. First, I only teseted this with Visual Studio. I am using VS2012, but the version you use should be the same.

Visual Studio comes with a utility called DUMPBIN.EXE, which can show you what symbols are exported from a DLL. Since I didn't use gcc, I don't know what it does, but apparently (as on Linux) it exports all functions. So MYDLL.DLL built from gcc (as C) should have the three functions exported.

When using VS, the __declspec(dllexport) is certainly required. Without it, the functions are not exported, and so not visible. The MYDLL.DLL built with VS (before adding the __declspec(dllexport) shows no functions visible, and so of course the runtime can't find them.

Adding the __declspec(dllexport), rebuilding, and again running dumpbin shows that the function names are mangled:

$ dumpbin /exports mydll.dll

Microsoft (R) COFF/PE Dumper Version 10.00.40219.01

Copyright (C) Microsoft Corporation.  All rights reserved.

Dump of file mydll.dll

File Type: DLL

 Section contains the following exports for MyDll.dll

   00000000 characteristics

   525ED4A3 time date stamp Wed Oct 16 11:02:11 2013

       0.00 version

          1 ordinal base

          3 number of functions

          3 number of names

   ordinal hint RVA      name

         1    0 0001118B ?get_string@@YAHPADJ@Z = @ILT+390(?get_string@@YAHPADJ@Z)

         2    1 00011195 ?multiply_double@@YAHPAN00@Z = @ILT+400(?multiply_double@@YAHPAN00@Z)

         3    2 00011168 ?multiply_long@@YAHJJPAJ@Z = @ILT+355(?multiply_long@@YAHJJPAJ@Z)

 Summary

       1000 .data

       1000 .idata

       3000 .rdata

       1000 .reloc

       1000 .rsrc

       7000 .text

      10000 .textbss

The __cdecl addition you made does nothing to the names. That just specifies the calling convention (which is important!, but not yet). Instead, you need to make sure the names are not mangled. The way I found to do this is to surround the majority of the file with extern "C":

extern "C" {

__declspec(dllexport) int multiply_long(long x, long y, long *z)

{

   *z = x * y;

   return 0;

}

[snip]

   return 0;

} // end of function get_string

} // matches extern "C" above

Now when I build the dll and run the COBOL program, I get an assertion failure because the length you pass to get_string() is too small for the string you are trying to copy to the buffer. But the other two functions show the expected results.

So I believe this answers your question.

Note that extern "C"{} should also work with gcc, as this is part of the C++ standard.

View solution in original post

0 Likes
7 Replies
Micro Focus Contributor
Micro Focus Contributor

RE: Calling C++ functions from COBOL

Jump to solution

Rather than me trying to come up with an artificial example, could you post a simple C++ file, your compile and link options, and a COBOL program that fails to call the function? I believe I would be able to then modify the source and/or the COBOL program in such a way that COBOL can call the function.

0 Likes
mfisher Absent Member.
Absent Member.

RE: Calling C++ functions from COBOL

Jump to solution

Sure. First of all I'll show what works. Here is a C file called mydll.c:

#include <string.h>

int multiply_long(long x, long y, long *z)

{

 *z = x * y;

 return 0;

}

int multiply_double(double *x, double *y, double *z)

{

 *z = (*x) * (*y);

 return 0;

}

int get_string(char *s, long len)

{

 strncpy(s, "Hello, world! This is a string to be copied.", len);

 return 0;

}

=====

Here is a COBOL program called dlltest.cbl:

       IDENTIFICATION DIVISION.

       PROGRAM-ID.  DLLTEST.

       DATA DIVISION.

       WORKING-STORAGE SECTION.

       77  XL   SIGNED-LONG.

       77  YL   SIGNED-LONG.

       77  ZL   SIGNED-LONG.

       77  XD   DOUBLE.

       77  YD   DOUBLE.

       77  ZD   DOUBLE.

       77  STR  PIC X(80).

       77  LEN  SIGNED-LONG.

       PROCEDURE DIVISION.

           CALL "mydll.dll"

           MOVE 2 TO XL

           MOVE 3 TO YL

           CALL "multiply_long"

             USING BY VALUE XL, YL

                   BY REFERENCE ZL

             GIVING RETURN-CODE

           END-CALL

           DISPLAY XL "x" YL "=" ZL

           MOVE 1.2 TO XD,

           MOVE 3.4 TO YD

     * DOUBLE data items cannot be passed BY VALUE.

           CALL "multiply_double"

             USING BY REFERENCE XD, YD, ZD

             GIVING RETURN-CODE

           END-CALL

           DISPLAY XD "x" YD "=" ZD

           MOVE ZEROS TO STR

           MOVE 13 TO LEN

           CALL "get_string"

             USING BY REFERENCE STR

             BY VALUE LEN

             GIVING RETURN-CODE

           END-CALL

           DISPLAY STR(1:LEN)

           ACCEPT OMITTED

           CANCEL "mydll.dll"

           GOBACK.

=====

I tested the code shown above in Windows 7 as follows:

ACUCOBOL-GT 8.1.3.1 is already installed.

Install gcc (e.g., the version of gcc that ships in the MinGW flavor of Qt 5.1).

Set the environment variables MINGW and ACU to point to the relevant bin directories.

Open a command prompt, and execute:

%MINGW%\gcc -c mydll.c

%MINGW%\gcc -shared -o mydll.dll mydll.o

%ACU%\ccbl32 dlltest.cbl

%ACU%\wrun32 dlltest

The output is as expected.

=====

I will paste in my C++ example next.

0 Likes
mfisher Absent Member.
Absent Member.

RE: Calling C++ functions from COBOL

Jump to solution

There is no need to compile the C code shown above as C++. However, my goal is eventually to write a C wrapper that provides a C interface to a library written in C++.

=====

A first simple-minded attempt to compile the C code as C++ is:

%MINGW%\g++ -c -x c++ mydll.c

%MINGW%\g++ -shared -o mydll.dll mydll.o

%ACU%\ccbl32 dlltest.cbl

%ACU%\wrun32 dlltest

=====

This yields the following error from wrun32:

---------------------------

Error

---------------------------

multiply_long: Program missing or inaccessible

COBOL error at 000025 in dlltest

---------------------------

OK  

---------------------------

0 Likes
mfisher Absent Member.
Absent Member.

RE: Calling C++ functions from COBOL

Jump to solution

A second simple-minded attempt in Visual Studio 2013 RC..

In Visual Studio 2013 RC:

File > New Project > Visual C++ > Win32 > Win32 Project.

As the project name, enter "mydll".

Click OK > Next, and select DLL as the application type.

Click Finish.

Paste the following code into the stub file mydll.cpp.

By way of example, the implementation of the get_string() function below uses the C++ string type.

=====

// mydll.cpp : Defines the exported functions for the DLL application.

//

#include "stdafx.h"

#include <string>

#include <cstring>

int multiply_long(long x, long y, long *z)

{

*z = x * y;

return 0;

}

int multiply_double(double *x, double *y, double *z)

{

*z = (*x) * (*y);

return 0;

}

int get_string(char *s, long len)

{

std::string cpp_str = "Hello, world! This is a string to be copied.";

char *c_str = new char[cpp_str.length() + 1];

strncpy_s(s, len, c_str, len);

return 0;

}

=====

Click Project > Build to build the DLL.

Copy the DLL to the folder containing the COBOL test program.

Compile and run the COBOL program with:

%ACU%\ccbl32 dlltest.cbl

%ACU%\wrun32 dlltest

The same error as before occurs in wrun32:

---------------------------

Error

---------------------------

multiply_long: Program missing or inaccessible

COBOL error at 000025 in dlltest

---------------------------

OK  

---------------------------

0 Likes
mfisher Absent Member.
Absent Member.

RE: Calling C++ functions from COBOL

Jump to solution

Next I tried decorating the function signatures with __declspec(dllexport) and __cdecl. The Visual Studio version of the mydll.cpp file then becomes:

// mydll.cpp : Defines the exported functions for the DLL application.

//

#include "stdafx.h"

#include <string>

#include <cstring>

__declspec(dllexport) int __cdecl multiply_long(long x, long y, long *z)

{

*z = x * y;

return 0;

}

__declspec(dllexport) int __cdecl multiply_double(double *x, double *y, double *z)

{

*z = (*x) * (*y);

return 0;

}

__declspec(dllexport) int __cdecl get_string(char *s, long len)

{

std::string cpp_str = "Hello, world! This is a string to be copied.";

char *c_str = new char[cpp_str.length() + 1];

strncpy_s(s, len, c_str, len);

return 0;

}

=====

Build the DLL in Visual Studio.

Copy it to the directory containing the COBOL test program.

Execute:

%ACU%\ccbl32 dlltest.cbl

%ACU%\wrun32 dlltest

The error is the same as before.

Thank you for your help with this!

0 Likes
Micro Focus Contributor
Micro Focus Contributor

RE: Calling C++ functions from COBOL

Jump to solution

Got it. First, I only teseted this with Visual Studio. I am using VS2012, but the version you use should be the same.

Visual Studio comes with a utility called DUMPBIN.EXE, which can show you what symbols are exported from a DLL. Since I didn't use gcc, I don't know what it does, but apparently (as on Linux) it exports all functions. So MYDLL.DLL built from gcc (as C) should have the three functions exported.

When using VS, the __declspec(dllexport) is certainly required. Without it, the functions are not exported, and so not visible. The MYDLL.DLL built with VS (before adding the __declspec(dllexport) shows no functions visible, and so of course the runtime can't find them.

Adding the __declspec(dllexport), rebuilding, and again running dumpbin shows that the function names are mangled:

$ dumpbin /exports mydll.dll

Microsoft (R) COFF/PE Dumper Version 10.00.40219.01

Copyright (C) Microsoft Corporation.  All rights reserved.

Dump of file mydll.dll

File Type: DLL

 Section contains the following exports for MyDll.dll

   00000000 characteristics

   525ED4A3 time date stamp Wed Oct 16 11:02:11 2013

       0.00 version

          1 ordinal base

          3 number of functions

          3 number of names

   ordinal hint RVA      name

         1    0 0001118B ?get_string@@YAHPADJ@Z = @ILT+390(?get_string@@YAHPADJ@Z)

         2    1 00011195 ?multiply_double@@YAHPAN00@Z = @ILT+400(?multiply_double@@YAHPAN00@Z)

         3    2 00011168 ?multiply_long@@YAHJJPAJ@Z = @ILT+355(?multiply_long@@YAHJJPAJ@Z)

 Summary

       1000 .data

       1000 .idata

       3000 .rdata

       1000 .reloc

       1000 .rsrc

       7000 .text

      10000 .textbss

The __cdecl addition you made does nothing to the names. That just specifies the calling convention (which is important!, but not yet). Instead, you need to make sure the names are not mangled. The way I found to do this is to surround the majority of the file with extern "C":

extern "C" {

__declspec(dllexport) int multiply_long(long x, long y, long *z)

{

   *z = x * y;

   return 0;

}

[snip]

   return 0;

} // end of function get_string

} // matches extern "C" above

Now when I build the dll and run the COBOL program, I get an assertion failure because the length you pass to get_string() is too small for the string you are trying to copy to the buffer. But the other two functions show the expected results.

So I believe this answers your question.

Note that extern "C"{} should also work with gcc, as this is part of the C++ standard.

View solution in original post

0 Likes
mfisher Absent Member.
Absent Member.

RE: Calling C++ functions from COBOL

Jump to solution

It does work with g++ as well. I had tried using extern "C", but must have made some other error. Your code sample did the trick, which gets me past what had been a total roadblock. Thank you!

0 Likes
The opinions expressed above are the personal opinions of the authors, not of Micro Focus. By using this site, you accept the Terms of Use and Rules of Participation. Certain versions of content ("Material") accessible here may contain branding from Hewlett-Packard Company (now HP Inc.) and Hewlett Packard Enterprise Company. As of September 1, 2017, the Material is now offered by Micro Focus, a separately owned and operated company. Any reference to the HP and Hewlett Packard Enterprise/HPE marks is historical in nature, and the HP and Hewlett Packard Enterprise/HPE marks are the property of their respective owners.