我有一个C#(NET6)项目,在该项目中我try 调用Fortran子 routine 并传递一个struct. 我在这里做了简化版本:

克:

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

使用MaxSize = 45行,而不是46行.从46开始,我会遇到"Stack Overflow"异常/崩溃.45岁时,祖父母的年龄是729000岁. 我曾try 在Visual Studio2022的Fortran项目属性下,将配置属性-&>优化-&>堆数组设置为0(表示始终在堆上分配数组)以及配置属性-&>链接器-&>系统-&>堆栈保留大小=MaxSize = 45000000,我认为这已经足够了,但无济于事.

我开始认为它应该在c#项目中完成,但我不知道在哪里. 有谁知道可以做些什么吗?

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".

在(首次)try 反汇编窗口之后 我发现错误在倒数第二行抛出,如下所示(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  

谈到拆卸,我是个新手,但也许这给了别人一个提示……

推荐答案

Fortran编译器将该 struct 公开为引用.在(Intel Here)编译和反汇编后,可以看到下面的GRANDPARANT *GP:

enter image description here

因此,C#声明应该改为:

public static extern void SizeCheck(ref GrandParent gp);

PS:调用约定在x64中并不重要(fastcall等于by default)

要修复堆栈溢出,您可以1)使用Visual Studio SDK中的EditBin更改编译到可执行文件中的最大堆栈大小,例如(使用10Mb的堆栈):

EditBin.exe <your.exe> /stack:10000000

或者2)决定像这样"手动"编组大对象:

var gp = new GrandParent();
var ptr = Marshal.AllocCoTaskMem(Marshal.SizeOf<GrandParent>());
try
{
    Marshal.StructureToPtr(gp, ptr, false);
    Natives.SizeCheck(ptr);
}
finally
{
    Marshal.FreeCoTaskMem(ptr);
}

对DllImport语句进行如下修改:

[DllImport("FortranLib.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void SizeCheck(IntPtr gp);

Csharp相关问答推荐

为什么使用DXGI输出复制和Direct 3D时捕获的图像数据全为零?

FromServices不使用WebAppliationFactory程序>

ASP.NET Core:如何在IPageFilter中注入ApplicationDbContext

编写DataAnnotations自定义验证器的多种方法

一种安全的方式来存储SSH凭证(MAUI/C#应用程序)

如何从HttpContext获取请求正文

try 在Blazor项目中生成html

MS Graph v5.42.0:在寻呼消息时更改页面大小

在.NET核心项目中创建Startup.cs比在Program.cs中注册服务好吗?

为什么C#认为这个非托管 struct 有一个重叠

DateTime ToString()未以指定格式打印

泛型参数在.NET 8 AOT中没有匹配的批注

如何在onNext之前等待订阅者完成?

.Net MAUI,在将FlyoutPage添加到容器之前,必须设置添加构造函数导致Flyout和Detail"

为什么ReadOnlySpan;T&>没有Slice(...)的重载接受Range实例的?

如何在发布NuGet包之前设置命名空间?

从HTML元素获取 colored颜色

客户端/服务器RPC如何处理全局变量?

为什么INTEGER在通过反射调用时对空对象返回TRUE,而在INTEGER上调用时返回FALSE?

在Swagger中显示自定义属性的属性名称