I have a C# (NET 6) project where I try to call fortran subroutine and passing a struct.
I have made simplified version here:
c#:
using System.Runtime.InteropServices;
Console.WriteLine("C# starting");
Console.WriteLine($"Size of GrandParent: {Marshal.SizeOf(typeof(GrandParent))}");
var gp = new GrandParent();
Natives.SizeCheck(gp);
public static class Natives
{
public const int MaxSize = 100;
[DllImport(dllName: "FortranLib.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void SizeCheck(GrandParent gp);
}
[StructLayout(LayoutKind.Sequential)]
public struct Child
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = Natives.MaxSize)]
internal double[] Array;
public Child()
{
Array = new double[Natives.MaxSize];
}
}
[StructLayout(LayoutKind.Sequential)]
public struct Parent
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = Natives.MaxSize)]
internal Child[] Children;
public Parent()
{
Children = new Child[Natives.MaxSize];
}
}
[StructLayout(LayoutKind.Sequential)]
public struct GrandParent
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = Natives.MaxSize)]
internal Parent[] Children;
public GrandParent()
{
Children = new Parent[Natives.MaxSize];
}
}
and the FortranLib.dll code:
module FortranLib
use ISO_C_BINDING
implicit none
integer,parameter :: MaxSize = 100
type, bind(C) :: Child
real(C_DOUBLE) :: Array(MaxSize)
end type Child
type, bind(C) :: Parent
type(Child) :: Children(MaxSize)
end type Parent
type, bind(C) :: GrandParent
type(Parent) :: Children(MaxSize)
end type GrandParent
contains
subroutine SizeCheck(gp)
!DEC$ ATTRIBUTES DLLEXPORT::SizeCheck
!DEC$ ATTRIBUTES DECORATE,ALIAS:"SizeCheck" :: SizeCheck
! Variables
type(GrandParent), intent(in) :: gp
! Body of SizeCheck
write(*,*) "In fortran dll first line"
write(*,*) "Number of parents: ", size(gp%Children)
write(*,*) "Number of children for each parent : ", size(gp%Children(0)%Children)
write(*,*) "Size of child array : ", size(gp%Children(0)%Children(0)%Array)
end subroutine SizeCheck
end module FortranLib
Using a MaxSize = 45 works but not 46. From 46 and up I get a "Stack overflow" exception / crash. At 45 the size of GrandParent is 729000.
I have tried to set, in Visual Studio 2022 under the fortran project properties, Configuration Properties -> Optimization -> Heap Arrays = 0 (for always allocate arrays on heap) as well as Configuration Properties -> Linker -> System -> Stack Reserve Size = 100000000 which I thought would be enough, but to no avail.
I start to think that it should be done in the c# project but where I do not know.. Anyone have an idea of what could be done?
EDIT 2022-09-09:
After adding `ref` to the `SizeCheck` parameter in c# it now works up to `MaxSize = 57` but larger values still crash with "Stack overflow".
After (for the first time) trying out disassembly window
I found that the error is thrown at the second to last line shown here ( 00007FF8CB397F98 E8 73 FC FF FF call CLRStub[MethodDescPrestub]@7ff8cb397c10 (07FF8CB397C10h) ):
5: var gp = new GrandParent();
00007FF8CB397F8B 48 8D 4D 78 lea rcx,[rbp+78h]
00007FF8CB397F8F E8 BC F4 FF FF call Method stub for: GrandParent..ctor() (07FF8CB397450h)
6: Natives.SizeCheck(ref gp);
00007FF8CB397F94 48 8D 4D 78 lea rcx,[rbp+78h]
00007FF8CB397F98 E8 73 FC FF FF call CLRStub[MethodDescPrestub]@7ff8cb397c10 (07FF8CB397C10h)
00007FF8CB397F9D 90 nop
I am a n00b when it comes to disassembly but perhaps it gives someone else a clue...
