Highlighted
Micro Focus Contributor
Micro Focus Contributor
1972 views

How to pass a POINTER between COBOL and C# in .NET?

Jump to solution

[Migrated content. Thread originally posted on 04 August 2011]

SI 2529598
Client writes:
I want to define a COBOL class under .NET that allocates memory (via CBL_ALLOC_MEM) and passes the pointer to the allocated memory back to a c# calling routine.

I spoke to develoeprs and they were not sure if what was being done was the best approach, it was suggested that a Structure should be used to pass the data, below I am providing a sample (the developer provided this) for you to use/review:


You could use C# structs for example:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

using MicroFocus.COBOL.RuntimeServices;

namespace ConsoleApplication1
{
class Program
{
[Serializable]
[StructLayout(LayoutKind.Sequential)]
struct BCM
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
public byte[] ESeguib;

[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
public byte[] Nome;

public BCM(byte[] a, byte[] b)
{
this.ESeguib = a;
this.Nome = b;
}

public BCM(string a, string b)
{
this.ESeguib =
System.Text.Encoding.ASCII.GetBytes(string.Format("{0,-8}", a));
this.Nome =
System.Text.Encoding.ASCII.GetBytes(string.Format("{0,-8}", b));
}

public string ToString()
{
return System.Text.Encoding.ASCII.GetString(this.ESeguib)
+ System.Text.Encoding.ASCII.GetString(this.Nome);
}
}

static void Main(string[] args)
{
BCM x = new BCM("a", "bb");
byte[] xInBytes = RawSerialize(x);

RunUnit MyRunUnit = new RunUnit();
try
{
MyProgram prog = new MyProgram();
MyRunUnit.Add(prog);
MyRunUnit.Call("MyProgram", xInBytes);
x = (BCM)RawDeserialize(xInBytes, 0, typeof(BCM));
}
finally
{
if (MyRunUnit != null)
{
MyRunUnit.StopRun();
}
}
}


public static byte[] RawSerialize(object anything)
{
int rawSize = Marshal.SizeOf(anything);
IntPtr buffer = Marshal.AllocHGlobal(rawSize);
Marshal.StructureToPtr(anything, buffer, false);
byte[] rawDatas = new byte[rawSize];
Marshal.Copy(buffer, rawDatas, 0, rawSize);
Marshal.FreeHGlobal(buffer);
return rawDatas;
}

public static object RawDeserialize(byte[] rawData, int position,
Type anyType)
{
int rawsize = Marshal.SizeOf(anyType);
if (rawsize > rawData.Length)
return null;
IntPtr buffer = Marshal.AllocHGlobal(rawsize);
Marshal.Copy(rawData, position, buffer, rawsize);
object retobj = Marshal.PtrToStructure(buffer, anyType);
Marshal.FreeHGlobal(buffer);
return retobj;
}
}
}

and the COBOL being:
program-id. MyProgram.

linkage section.
01 BCM.
03 BCM-R.
05 BCM-PROGRAMMA.
07 BCM-ESEGUIB PIC X(8).
07 BCM-NOME PIC X(8).

procedure division using BCM.
move ALL "A" to BCM-ESEGUIB
move ALL "B" to BCM-NOME
call "xx" on exception
continue

goback.
--

The example provided still does not deal with the POINTER issue – as far as the Client can see. Client really needs an example of passing a pointer between C# (or just .NET ) and COBOL. The internet seems to be devoid of any examples but does suggest it is tricky.

Client thought for a while that he could keep the pointer as instance data but it is required by the C# program. Basically, the C# program calls the COBOL program to allocate memory. The memory pointer is passed back to C# and it (later on) “populates” data in a COBOL structure. The (copybook) structure contains a POINTER data type which is populated with the value passed to C#. So C# does not operate on the data referenced by the pointer but passes it to a different (legacy) COBOL program which does.

MemoryManager is trying to overlay a 4-byte structure, ptroverlay PIC X(4), and then pass this to a .NET data type, MemPointer binary-char unsigned occurs any, which occurs in LINKAGE. This
“set MemPointer to ptroverlay”
statement causes a Visual Studio internal error at runtime in the demo sent. Client assume it “works” at runtime because there is less checking.

Could anyone provide more information or a sample that would answer the question?
0 Likes
1 Solution

Accepted Solutions
Highlighted
Micro Focus Expert
Micro Focus Expert
Time to explain a little further...

In managed code, a COBOL pointer is a virtual address into our own virtual memory map. This virtual address is associated with a byte[] and offset. In managed code the CBL_ALLOC_MEM creates the byte[] + offset 0 and generates a virtual address for it and then gives this address back to the COBOL pointer (plus doing other things for shared memory etc..).

Because this virtual address map is not a "native address" map you can not map a COBOL pointer to a native block of memory aka a System.IntPtr. The only part of the system that understands this relationship is the .Net runtime itself and unfortunately we do not expose any interop mechanism for translating a COBOL pointer to a native pointer.

Therefore if you want to pass a group item that contains COBOL pointer from managed code to native code, you will need to manage this yourself. Which is possible but not an easy task todo. I would recommend avoiding passing COBOL pointers in group items between managed/native code. The easiest approach is to pass the pointer as an extra parameter or use the more natural .Net approach of using valuetypes/structs and letting the CLR's own marshaller do the work for you.

View solution in original post

0 Likes
2 Replies
Highlighted
Micro Focus Contributor
Micro Focus Contributor
Hi Kim,

I have to ask why, oh why, oh why?!!? What are they hoping to achieve? They would be better advised to pass back a byte array for all sorts of reasons:

1. c# will be able to access the data in a simpler manner
2. the requirement of pinning the allocated data promotes memory fragmentation
3. the code will need to be marked unsafe
4. it WILL be unsafe!!!!

Fiddling round with pointers in the .NET environment is sometimes (rarely) necessary, but is more often a sign of someone who doesn't understand .NET or object oriented programming ...

Sorry if I've been a bit blunt!

Michael
0 Likes
Highlighted
Micro Focus Expert
Micro Focus Expert
Time to explain a little further...

In managed code, a COBOL pointer is a virtual address into our own virtual memory map. This virtual address is associated with a byte[] and offset. In managed code the CBL_ALLOC_MEM creates the byte[] + offset 0 and generates a virtual address for it and then gives this address back to the COBOL pointer (plus doing other things for shared memory etc..).

Because this virtual address map is not a "native address" map you can not map a COBOL pointer to a native block of memory aka a System.IntPtr. The only part of the system that understands this relationship is the .Net runtime itself and unfortunately we do not expose any interop mechanism for translating a COBOL pointer to a native pointer.

Therefore if you want to pass a group item that contains COBOL pointer from managed code to native code, you will need to manage this yourself. Which is possible but not an easy task todo. I would recommend avoiding passing COBOL pointers in group items between managed/native code. The easiest approach is to pass the pointer as an extra parameter or use the more natural .Net approach of using valuetypes/structs and letting the CLR's own marshaller do the work for you.

View solution in original post

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.