Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.
29040 Discussions

Passing an array of structs from C# to Fortran

Jan66
Beginner
115 Views

Hello all

I have been struggling to pass an array of structs from C# to a Fortran DLL, alter its contents in Fortran and pass it back again. I managed to get it more or less working, but I'm apparently using an onconventional way of calling. And though it seems to work fine, I do get an 'imbalanced stack' error in C# debug mode. In Fortran debug mode it seems to work fine however.

Here is my Fortran header:

SUBROUTINE PassArrayOfStructs(pArray, Length)
cDEC$ ATTRIBUTES DLLEXPORT :: PassArrayOfStructs
cDEC$ ATTRIBUTES DECORATE, ALIAS: "PassArrayOfStructs"
& :: PassArrayOfStructs
USE ISO_C_BINDING, ONLY: C_INT, C_PTR

TYPE(C_PTR), INTENT(IN):: pArray [VALUE] !the external pointer to the external array - be sure to pass it by value!
INTEGER(C_INT), INTENT(IN):: Length [VALUE] !the length of the external array

And my C# header:

[DllImport(Constants.DLL_NAME, CallingConvention = CallingConvention.Cdecl)]
public static extern void PassArrayOfStructs(IntPtr pArray, int length);

and its call:

public static SelectionResult[] SelectNew(int nSolutions)
{
SelectionResult[] resultList = new SelectionResult[nSolutions];
 
  //JUST FOR TESTING: initialize the array with some arbitrary values:
  for (int i = 0; i < resultList.Length; i++)
{
resultList[i].RecID = (int)(123 * i);
    resultList[i].Alfa = (float)(3.14 * (i + 1));
resultList[i].FSP = (float)(2.5 * (i + 1));
    resultList[i].Q = (float)(18.6 * (i^2 + 1));
  }
 
  int structSize = Marshal.SizeOf(typeof(SelectionResult)); //the size of the struct in bytes
  int arraySize = resultList.Length * structSize; //the length (i.e. number of elements) of the array of structs
 
  IntPtr pArray = Marshal.AllocHGlobal(arraySize); //allocate the required memory for the whole array and return a pointer to it
 
  try
  {
    for (int i = 0; i < resultList.Length; i++) //copy the data from each array element to the allocated memory
    {
      IntPtr structPtr = new IntPtr(pArray.ToInt64() + i * structSize); //return a pointer to the memory block required to hold the contents of the struct
      Marshal.StructureToPtr(resultList[i], structPtr, false); //copy the actual data in the struct to the memory block
    }
 
Unsafe.PassArrayOfStructs(pArray, nSolutions);                         //pass the pointer to the array w\ structs and its length to the DLL
 
    // Copy the modified data back to the C# array
    for (int i = 0; i < nSolutions; i++)
    {
      resultList[i] = (SelectionResult)Marshal.PtrToStructure(pArray + i * structSize, typeof(SelectionResult));
    }
 
  }
  finally
  {
    Marshal.FreeHGlobal(pArray); //free the allocated memory
  }
return resultList;
}

 

I do have several questions:

1. Why do I have to add the [VALUE]  attribute when passing the pointer to get it working, though this is against the standard calling conventions?

2. If I try to pass the 'Length' (or 'pArray' for that matter) argument as 'INTEGER(C_INT), INTENT(IN), VALUE:: Length' I get a 'trying to write protected memory' error and the program crashes, though this is the standard syntax I should be using (as I have been told).

3. What could cause the 'imbalanced stack' warning? Could it be that this is something that is caused by the Debug environment?

Further information:

- I compile in 32-bit mode.

- I'm  using the Intel Parallel Studio XE 2029 compiler, integrated in Visual Studio 2022.

 

Your help is much appreciated!

Regards, Jan

 

Labels (1)
0 Kudos
0 Replies
Reply