Highlighted
New Member.
4066 views

Calling dll's

I don't know why it has to be so complicated to call a dll from acucobol 😞 Anyway I now have to make a call to a dll function that will populate an array of items defined as follows.

Function Call:
Input  (int64)
Output (Pointer to structure1)

Structure1
Items      OutItem(10)
ItemsCount Uint8

outItem
Total      Double
String1    Char(36) Null terminated string, with a buffer maximum length of 'n+1'
String2    Char(10) Null terminated string, with a buffer maximum length of 'n+1'


Here is how I defined it and make the call but of course it does not work, the dll throws a memory access violation writing to address 00000000. I'm assuming the problem is that a cobol OCCURS does not translate well to the dll's ARRAY. Any direction would be helpful...

77  WS-RETVAL    PIC S9(9) COMP-5. 
77  WS-NUM       PIC 9(18) COMP-5.
01  WS-Structure1.
    03 WS-Items OCCURS 10.  
       05 WS-Item-Total   DOUBLE.
       05 WS-Item-Str1    PIC X(37).
       05 WS-Item-Str2    PIC X(11).
    03 WS-ItemsCount      PIC X(1)   COMP-N.

PERFORM VARYING CTR FROM 1 BY 1 UNTIL CTR > 10
    MOVE ZEROS      TO WS-Item-Total
    MOVE LOW-VALUES TO WS-Item-Str1(CTR)(37:1)
    MOVE LOW-VALUES TO WS-Item-Str1(CTR)(11:1)
END-PERFORM.
MOVE ZEROS TO WS-ItemsCount.

CALL "Lib.dll@WINAPI".

CALL "Function"
   USING BY VALUE WS-NUM
         BY REFERENCE WS-Structure1
   GIVING WS-RETVAL
END-CALL.

CANCEL "Lib.dll@WINAPI".

0 Likes
11 Replies
Highlighted
Micro Focus Expert
Micro Focus Expert

RE: Calling dll's

Actually, calling DLLs is not so hard. We have many, many customers doing it all the time.

The hard part, which you have been hitting lately, has to do with matching C data types with COBOL data types, and in particular C structures and COBOL group items.

Sadly, both languages are affected by compile switches. So modifying a compile switch (in either C or COBOL) can affect the layout of the structure or group item.

I assume you don't have the ability to modify the DLL in any way. So that means modifying how the ACUCOBOL-GT compiler lays out group items. In this, examining the layout with a source listing is an essential tool.

Normally, DLLs like this will come with header files that describe the structures that are used as arguments. If I were trying to write the COBOL to match the C structure, especially one this complex, I would start with a C program and examine the addresses of all of the elements, in order to determine exactly how the C compiler laid out the structure. (C compilers will normally add padding to things like this, but the exact amount of padding depends on a lot of things. And by looking at how that padding is done, I would have a better idea about the next step.)

Once I had the relative addresses of all of the structure elements, I would start using SYNC in various places in the COBOL group, in order to get the relative addresses of the elementary items to match the relative addresses of the C structure..

It looks to me like you are close with the COBOL group item you have defined. But without more information about the DLL, I would have a hard time providing further advice.

0 Likes
Highlighted
Absent Member.
Absent Member.

RE: Calling dll's

You can't write to position 37 of an array of 36 char, or to position 11 of an array of 10 char. Those would indeed be memory access violations.

If your intention is to initialize each string to an empty string, you should simply do this:

MOVE LOW-VALUES TO WS-Item-Str1(CTR)

MOVE LOW-VALUES TO WS-Item-Str2(CTR)

or

MOVE LOW-VALUES TO WS-Item-Str1(CTR)(1:1)

MOVE LOW-VALUES TO WS-Item-Str2(CTR)(1:1)

Incidentally, there is a typographical error in your code here because in both cases you're moving a low value to a member of WS-Item-Str1. You obviously meant that the data item in the second command should be WS-Item-Str2.

If your intention was to place a null terminator at the end of non-empty strings, you should do something like this instead:

INSPECT WS-Item-Str1(CTR) REPLACING TRAILING SPACES BY LOW-VALUES

INSPECT WS-Item-Str2(CTR) REPLACING TRAILING SPACES BY LOW-VALUES

If you haven't previously worked with C strings, the relevant concepts are:

  • The array size must be 1 char longer than the longest value it will contain -- and the same must also be true of the corresponding COBOL data items.
  • The null terminator should be placed after the last meaningful character, or in an empty array there should be a null character in the first position.
  • Typically trailing spaces in COBOL data items are not meaningful, and so you would replace trailing spaces by low values.

0 Likes
Highlighted
Absent Member.
Absent Member.

RE: Calling dll's

Note also that the following line from your code does not compile:

MOVE ZEROS      TO WS-Item-Total

It should be:

MOVE ZEROS      TO WS-Item-Total(CTR)

A test program written as follows both compiles and runs without errors:

IDENTIFICATION DIVISION.

      PROGRAM-ID. HELLO-DLL.

      DATA DIVISION.

      WORKING-STORAGE SECTION.

      77  CTR  PIC S9(9) COMP-5.

      01  WS-Structure1.

          03 WS-Items OCCURS 10.

             05 WS-Item-Total   DOUBLE.

             05 WS-Item-Str1    PIC X(37).

             05 WS-Item-Str2    PIC X(11).

          03 WS-ItemsCount      PIC X(1)   COMP-N.

      PROCEDURE DIVISION.

          PERFORM VARYING CTR FROM 1 BY 1 UNTIL CTR > 10

              MOVE ZEROS      TO WS-Item-Total(CTR)

              MOVE LOW-VALUES TO WS-Item-Str1(CTR)(37:1)

              MOVE LOW-VALUES TO WS-Item-Str1(CTR)(11:1)

          END-PERFORM.

          MOVE ZEROS TO WS-ItemsCount.

          GOBACK.

0 Likes
Highlighted
Absent Member.
Absent Member.

RE: Calling dll's

Hmm. The working code that I posted above actually shows that my first reply was off-base. You are not writing past the end of the array in those statements.

What the working code shows is that the error occurs somewhere in code that I could not test, namely:

CALL "Lib.dll@WINAPI".

CALL "Function"

  USING BY VALUE WS-NUM

        BY REFERENCE WS-Structure1

  GIVING WS-RETVAL

END-CALL.

CANCEL "Lib.dll@WINAPI".

Most likely it is the function call that crashes your program and you will probably need to follow rzack's advice above. I'll have to leave it at that since I'm merely a member of the community and must get back to my own work. Good luck!

0 Likes
Highlighted
New Member.

RE: Calling dll's

Thanks for the replies mfisher, I realize some of my syntax was incorrect. I didn't actually compile that code myself and only entered it from memory 🙂 I was pretty close tho you must admit. 

0 Likes
Highlighted
New Member.

RE: Calling dll's

[quote user="rzack"]

Actually, calling DLLs is not so hard. We have many, many customers doing it all the time.

[/quote]

We also use plenty of dll's this custom one in particular is giving me trouble.

[quote user="rzack"]

The hard part, which you have been hitting lately, has to do with matching C data types with COBOL data types, and in particular C structures and COBOL group items.

Sadly, both languages are affected by compile switches. So modifying a compile switch (in either C or COBOL) can affect the layout of the structure or group item.

I assume you don't have the ability to modify the DLL in any way. So that means modifying how the ACUCOBOL-GT compiler lays out group items. In this, examining the layout with a source listing is an essential tool.

[/quote]

You are correct I cannot change the dll, it is not mine. I have been toold to use the Listing option -Ls and yes that helps to verify that all my defined variables are using the correct number of bytes.

[quote user="rzack"]

Normally, DLLs like this will come with header files that describe the structures that are used as arguments. If I were trying to write the COBOL to match the C structure, especially one this complex, I would start with a C program and examine the addresses of all of the elements, in order to determine exactly how the C compiler laid out the structure. (C compilers will normally add padding to things like this, but the exact amount of padding depends on a lot of things. And by looking at how that padding is done, I would have a better idea about the next step.)

[/quote]

I do not currently have access or the time to mess around with a C compiler.  I am trying to communicate with the creator of the dll so that I can get the correct byte information. The documentation is not complete and appears to be missing some information.

[quote user="rzack"]

Once I had the relative addresses of all of the structure elements, I would start using SYNC in various places in the COBOL group, in order to get the relative addresses of the elementary items to match the relative addresses of the C structure..

[/quote]

What is SYNC? I have never heard of this.

[quote user="rzack"]

It looks to me like you are close with the COBOL group item you have defined. But without more information about the DLL, I would have a hard time providing further advice.

[/quote]

I know I am close. I just wasn't entirely sure that a COBOL OCCURS would translate properly as an Array... I think it should tho right?

Thanks for your insight I do appreciate it

 

 

0 Likes
Highlighted
New Member.

RE: Calling dll's

I'm still struggling to get the byte alignment correct on my dll calls, and I have searched the forums for more information regarding calling dll's and I see many reccomendations to use COMP-N definitions instead of using the "c-style" definitions. 

It looks like COMP-N doesn't handle negative numbers very well so if I need the negative I must use COMP-5 is this correct?

What is the "actual" real difference in using the COMP-5/COMP-N instead of the C-style value?

Have I defined the COMP=5/COMP-N values correctly here so they take up the same number of bytes in a structure?

Can anyone fill in the COMP-5/COMP-N values for DOUBLE and FLOAT?

To use this: Instead of this:
PIC S9(18) COMP-5 SIGNED-LONG
PIC S9(9) COMP-5 SIGNED-INT
PIC S9(4) COMP-5 SIGNED-SHORT
PIC X(8) COMP-N UNSIGNED-LONG
PIC X(4) COMP-N UNSIGNED-INT
PIC X(2) COMP-N UNSIGNED-SHORT
??? DOUBLE
??? FLOAT

 

 

 

 

 

 

 

 

 

Again thanks for any input.

 

 

 

 

 

 

0 Likes
Highlighted
Micro Focus Expert
Micro Focus Expert

RE: Calling dll's

SYNC is short for SYNCHRONIZED. For information, see the ACUCOBOL-GT Reference Manual, section 5.1.7.11 (Data Division -> Record Description Entry -> Data Description Entry -> SYNCHRONIZED Clause).

0 Likes
Highlighted
Micro Focus Expert
Micro Focus Expert

RE: Calling dll's

The various COMP types are described very well (I think) in the Reference Manual, under the USAGE Clause section. Though, since you are communicating with a DLL that uses native types, I suggest that you use the native type designators (SIGNED-LONG, etc). In particular, for DOUBLE and FLOAT, that is required. There is no COMP type that corresponds to these native floating point types.

That being said, the mapping you have above is correct.

0 Likes
Highlighted
New Member.

RE: Calling dll's

Thanks for the information rzack, I check the documentation and although there is some information there regarding this, it doesn't appear to be detailed enough to me....

I have another couple of questions:

1) When passing a structure BY REFERENCE does it automatially allocate the memory before passing the pointer?

2) Should I be using M$ALLOC to allocate the memory and create a pointer? We have never used M$ALLOC on any other dll calls. I just see it's an option and wondered what the difference would be between using BY REFERENCE and using M$ALLOC.

0 Likes
Highlighted
Micro Focus Expert
Micro Focus Expert

RE: Calling dll's

For completion, and in brief, SYNCHRONIZATION on data items refers to the alignment of those data items in memory, in order to enhance performance when accessing those data items. In particular, FLOAT and DOUBLE data items (in C) need to be aligned in memory so that their memory location starts on a 4-byte or 8-byte boundary. The C compiler normally adds padding if needed, in order to satisfy this restriction. And in COBOL, the compiler does the same thing when you use the SYNCHRONIZE phrase.

If the C compiler and COBOL compiler don't map the data items in exactly the same way, then data won[t be passed between the languages correctly.

To answer your other questions,

1) When passing a data item BY REFERENCE (either an elementary data item, or a group item), only the address of the data item is actually passed. There is no need to allocate the memory for that address, either internally (by the runtime) or explicitly (by the COBOL program).

2) There is no need to use M$ALLOC to allocate memory. Passing a data item BY REFERENCE  does the right thing automatically. The difference between passing a data item BY REFERENCE and using M$ALLOC is just the amount of work you need to do up front. For example, the following snippets do the same thing (assuming the group item my-data-item has a size of 64):

   CALL "M$ALLOC" USING 64 GIVING my-data-ptr

   CALL "M$PUT" USING my-data-ptr, my-data-item

   CALL "my-dll-routine" USING BY VALUE my-data-ptr

   CALL "M$GET" USING my-data-ptr, my-data-item

   CALL "M$FREE" using my-data-ptr

or

   CALL "my-dll-routine" USING BY REFERENCE my-data-item

The first example does the following:

allocate memory

copy the contents of your data item to the allocated memory

call the DLL routine with the allocated memory

copy the contents of the allocated memory back to your data item.

free the memory (else you have a memory leak).

I think you can see that just calling the routine with the data item BY REFERENCE is much simpler.

Micro Focus does have a professional services group, who could probably help you get this done much more quickly than going back and forth in the forum. It seems to me like this might be your best course of action now, in order to get past this blockage. Just something to think about.

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.