C#调用Fortran DLL传递大结构体时栈溢出问题求助
.NET 6调用Fortran传递大结构体时栈溢出问题解决
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]; } }
FortranLib.dll代码
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
问题描述
我有一个.NET 6的C#项目,尝试调用Fortran子程序并传递嵌套结构体。当MaxSize设为45时程序正常运行,但设为46及以上时会触发栈溢出异常/崩溃(MaxSize=45时GrandParent的大小为729000字节)。我曾在Visual Studio 2022的Fortran项目中设置:
- 配置属性->优化->堆数组=0(始终在堆上分配数组)
- 配置属性->链接器->系统->堆栈保留大小=100000000
但均无效,怀疑需要在C#项目中调整,求可行解决方法。
2022-09-09更新:在C#的SizeCheck参数中添加ref后,MaxSize可支持到57,但更大的值仍会因栈溢出崩溃。查看反汇编窗口,错误在调用SizeCheck的行抛出:
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
解决方案
1. 改用堆分配结构体(最可靠方案)
C#结构体默认在栈上分配,超大结构体直接导致栈溢出。通过将结构体分配到堆上,用指针传递给Fortran彻底解决问题:
修改C#代码
using System.Runtime.InteropServices; Console.WriteLine("C# starting"); int structSize = Marshal.SizeOf(typeof(GrandParent)); Console.WriteLine($"Size of GrandParent: {structSize}"); // 将结构体分配到堆上 var gp = new GrandParent(); IntPtr gpPtr = Marshal.AllocHGlobal(structSize); Marshal.StructureToPtr(gp, gpPtr, false); // 传递指针给Fortran Natives.SizeCheck(gpPtr); // 使用后释放堆内存,避免泄漏 Marshal.FreeHGlobal(gpPtr); public static class Natives { public const int MaxSize = 100; // 修改为接收IntPtr类型参数 [DllImport(dllName: "FortranLib.dll", CallingConvention = CallingConvention.Cdecl)] public static extern void SizeCheck(IntPtr gp); } // 结构体定义保持不变 [StructLayout(LayoutKind.Sequential)] public struct Child { /* ... */ } [StructLayout(LayoutKind.Sequential)] public struct Parent { /* ... */ } [StructLayout(LayoutKind.Sequential)] public struct GrandParent { /* ... */ }
修改Fortran代码
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_ptr) !DEC$ ATTRIBUTES DLLEXPORT::SizeCheck !DEC$ ATTRIBUTES DECORATE,ALIAS:"SizeCheck" :: SizeCheck ! Variables type(GrandParent), pointer :: gp type(c_ptr), intent(in) :: gp_ptr ! 将C指针转换为Fortran可访问的指针 call c_f_pointer(gp_ptr, gp) ! Fortran数组默认是1-based索引,修正原代码的越界问题 write(*,*) "In fortran dll first line" write(*,*) "Number of parents: ", size(gp%Children) write(*,*) "Number of children for each parent : ", size(gp%Children(1)%Children) write(*,*) "Size of child array : ", size(gp%Children(1)%Children(1)%Array) end subroutine SizeCheck end module FortranLib
2. 调整C#项目栈大小(临时方案)
如果必须保持栈传递方式,可以增大C#项目的栈保留大小:
- 右键C#项目 → 属性 → 生成 → 点击「高级」按钮
- 在「高级生成设置」中,找到「堆栈大小」,输入更大的值(例如
100000000,单位为字节) - 注意:32位程序栈大小有上限(通常不超过1GB),且过大的栈会浪费系统资源。
3. 拆分大结构体(可选优化)
将嵌套的大数组拆分为多个独立参数传递,避免一次性传递超大结构体,减少栈内存压力。
内容的提问来源于stack exchange,提问作者Erik Thysell
相关产品推荐
相关产品推荐

