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

Passing an array of structs from C# to Fortran

Jan66
Beginner
154 Views

Hello all

I'm trying to pass an array of structs from C# to a Fortran DLL, alter its contents in the DLL and pass it back to C#. I managed to get it working, but I get an 'unbalanced stack' warning in C# debug mode. It keeps working however. In Fortran debug mode, I get no warning whatsoever.

I used some unconventional calling conventions to get it finally working 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
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];
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. I have to pass the pointer by value to get it working, though this is against the standard calling conventions. How is this possible?
  2. If I pass the 'Length' argument (or the pointer 'pArray' for that matter) as 'INTEGER(C_INT), INTENT(IN), VALUE:: Length' instead of 'INTEGER(C_INT), INTENT(IN):: Length [VALUE]', I get a 'trying to write protected memory' error and the program crashes, though the first call is conform the standard calling conventions. What is happening here?
  3. What could be the cause of the 'imbalanced stack' warning?

Additional information:

  • I use the Intel Fortran Classic (IFORT) compiler.
  • I compile in 32-bit mode

Your help would be greatly appreciated!

Best regards, Jan

 

Labels (2)
0 Kudos
0 Replies
Reply