C# 键入marshalling从C调用fortran子例程#

C# 键入marshalling从C调用fortran子例程#,c#,fortran,marshalling,C#,Fortran,Marshalling,我正在尝试使用p/invoke从C#代码调用FORTRAN77子例程——如果您感兴趣,我正在尝试包装ARPACK库()提供的一些功能。我有两个问题 首先,在这个上下文中,我找不到关于类型编组的清晰说明。更具体地说,以下是在我的FORTRAN子程序中声明的类型: subroutine getEigenVectors & ( Matrix, n, which, nev, ncv, maxn, maxnev, maxncv, ldv, v, d) c

我正在尝试使用p/invoke从C#代码调用FORTRAN77子例程——如果您感兴趣,我正在尝试包装ARPACK库()提供的一些功能。我有两个问题


首先,在这个上下文中,我找不到关于类型编组的清晰说明。更具体地说,以下是在我的FORTRAN子程序中声明的类型:

       subroutine getEigenVectors
      &   ( Matrix, n, which, nev, ncv, maxn, maxnev, maxncv, ldv, v, d)

 c     %------------------%
 c     | Scalar Arguments |
 c     %------------------%

       character        which*2
       integer          n, nev, maxn, maxnev, maxncv, ldv

 c     %-----------------%
 c     | Array Arguments |
 c     %-----------------%
 c
       Real           
      &                 Matrix(n,n), v(ldv,maxncv), d(maxncv,2)
我在这里找到了一些有价值的信息: ,从中我暗示(我可能错了)我应该使用:

  • [marshallas(UnmanagedType.I4)]int
    传递整数
  • [marshallas(UnmanagedType.LPArray)]字节[]
    以传入字符串
但是,我完全不知道如何处理
Real
数组有人对此有什么想法吗?


第二,我不知道我是否应该把我的论点作为参考。我对FORTRAN一点也不熟悉——我知道,这使任务有点困难;然而,只有ARPACK做了我想做的事情。我确实在某个地方读到过,尽管FORTRAN子程序默认将所有参数作为引用因此,我是否应该将所有参数作为引用传递?

提前感谢您的帮助! 纪尧姆


编辑(8/6/11)

这是我最后的结论:

    [DllImport("Arpack.dll", EntryPoint = "#140")]
    private static extern void getEigenVectors(
        [MarshalAs(UnmanagedType.LPArray)] ref float[,] matrix, 
        [MarshalAs(UnmanagedType.I4)] ref int n,
        [MarshalAs(UnmanagedType.LPArray)] ref byte[] which,
        [MarshalAs(UnmanagedType.I4)] int whichLength,
        [MarshalAs(UnmanagedType.I4)] ref int nev, 
        [MarshalAs(UnmanagedType.I4)] ref int ncv,
        [MarshalAs(UnmanagedType.I4)] ref int maxn,
        [MarshalAs(UnmanagedType.I4)] ref int maxnev,
        [MarshalAs(UnmanagedType.I4)] ref int maxncv,
        [MarshalAs(UnmanagedType.I4)] ref int ldv,
        [MarshalAs(UnmanagedType.LPArray)] ref float[,] v,
        [MarshalAs(UnmanagedType.LPArray)] ref float[,] d
    );
我在这里做了几件事:

  • 通过将
    int
    对象编组为
    UnmanagedType.I4
    以匹配FORTRAN
    integer
    对象
  • 传递大小为(m,n)的对象,并将其编组为
    UnmanagedType.LPArray
    以匹配FORTRAN
    Real(n,m)
    对象
  • 通过将获取的对象编组为
    非托管类型.LPArray
    以匹配FORTRAN
    字符*n
    对象。
    字节[]
    对象的计算如下:

    System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding();
    byte[] which = encoding.GetBytes(myString);
    
  • 按值传递
    int
    对象,并将其编组为
    UnmanagedType.I4
    以指示字符串的长度。请注意,我试图将该参数放在字符串后面以及参数列表的末尾
这是我最好的机会——无论是这个还是其他我尝试过的东西都不起作用。该方法将执行,但它会以超快的速度退出(当它应该执行一些非常严格的计算时)。此外,我的3个数组被转换成奇怪的一维数组。下面是调试器给我的信息:

有什么想法吗?

1)引用:

单精度,在C语言家族中称为“float”和 Fortran中的“real”或“real*4”。这是一种二进制格式,占用 32位(4字节),其有效位的精度为24位 (大约7位小数)

因此,将其作为一个浮点数进行编组。你可以测试一下,它是浮动的还是双精度的

2) 引用这句话:

Fortran 77使用所谓的引用调用范式。这意味着 而不仅仅是传递函数/子例程的值 参数(按值调用),参数的内存地址 (指针)被传递


通过引用传递每个参数。

我建议您从一些小测试代码开始。编译一个FORTRAN.dll,其中包含一些带有简单参数的子程序,并使用C#来实现调用。此外,您可能希望将带有多个参数的Fortran封装到单个结构中(
TYPE
keyword),这使得互操作变得更加容易

下面是一个工作示例,您可以使用它来了解它的工作原理

原始FORTRAN代码:

  SUBROUTINE CALC2(BLKL,BLKW, N_LAMINA,N_SLICE, LOAD, SLOP,SKW,    &
                    DIA1,DIA2, Y1, Y2, N1, N2, DROP1, DROP2,        &
                    PARRAY, P_MAX, P_MAX_INDEX, ENDEFCT)
  !DEC$ ATTRIBUTES DLLEXPORT :: CALC2
  !DEC$ ATTRIBUTES ALIAS:'CALC2' :: CALC2
  !DEC$ ATTRIBUTES VALUE :: BLKL, BLKW, N_LAMINA, N_SLICE, LOAD, SLOP, SKW
  !DEC$ ATTRIBUTES VALUE :: DIA1, DIA2, Y1, Y2, N1, N2
  IMPLICIT NONE
  INTEGER*4, INTENT(IN) ::N_LAMINA, N_SLICE
  REAL*4, INTENT(IN) :: BLKL, BLKW, LOAD, SLOP, SKW,     &
                        DIA1, DIA2, Y1, Y2, N1, N2,   &
                        DROP1(MAX_LAMINA), DROP2(MAX_LAMINA)
  REAL*4, INTENT(OUT):: PARRAY(MAX_PATCH), P_MAX
  INTEGER*4, INTENT(OUT) :: P_MAX_INDEX, ENDEFCT
  INTEGER*4 :: NDIAG, N_PATCH
  REAL*4 :: SLNG, SWID
  REAL*4 :: DROPS_1(MAX_LAMINA), DROPS_2(MAX_LAMINA)

...

  END SUBROUTINE CALC2
具有实数和整数形式的各种标量和数组值。例如,
DROP1
是输入1D数组<代码>阵列将二维阵列输出为一维阵列<代码>BLKL是输入浮动

注意
!DEC$ATTRIBUTES值
装饰,以避免将所有内容声明为
ref

在C#中,代码由

    [DllImport("mathlib.dll")]
    static extern void CALC2(float major_dim, float minor_dim, 
        int N_lamina, int N_slices, float total_load, 
        float slope, float skew, float diameter_1, float diameter_2, 
        float youngs_1, float youngs_2, float nu_1, float nu_2, 
        float[] drops_1, float[] drops_2, float[] pressures, 
        ref float p_max, ref int p_max_index, ref EndEffect end_effect);

...
   {
        float max_pressure = 0;
        int max_pressure_index = 0;
        float[] pressures = new float[Definition.MAX_PATCH];
        EndEffect end_effect = EndEffect.NO;

        CALC2(length, width, lamina_count, slice_count, load, slope, skew, 
            dia_1, dia_2, y_1, y_2, n_1, n_2, drops_1, drops_2, pressures, 
            ref max_pressure, ref max_pressure_index, ref end_effect);
    }

注意我没有传递任何字符串。

ARPACK真是太棒了。我对fortran 77代码所做的是通过f2c-a运行它,使其成为C代码,然后构建它。如果您熟悉C,那么您可能会发现这更简单。请注意,Fortran会在每个字符串中传递一个长度变量。根据编译器和编译器选项的不同,它可以在字符串后立即传递,也可以在末尾传递。例如,我认为不需要marshalas属性,int默认为I4。你的矩阵肯定不会像那样正确排列。您需要在调试器中运行arpack代码,并查看参数是如何到达的。为什么要这样使用utf8?ht。您需要在调试器中运行arpack代码,并查看参数是如何到达的。为什么要使用utf8?根据我的经验,使用
marshallas()
过度指定参数越多,我看到的故障就越多。仅添加绝对必要的属性。例如,
float
自动封送为
REAL
。另请参见编译器是否有
!DEC$ATTRIBUTE VALUE
规范减少C中
ref
参数的数量#不能将多维数组作为
float[,]
传递。另外,数组已经是引用,不需要
ref
。fortran
REAL::A(2,2)
作为带有4个元素的
float A[]
传递。按顺序排列,它们是A11
A21
A12
A22
,因为FORTRAN与C在元素顺序上不同。FORTRAN矩阵也很重要,别忘了!“所以把它作为一个浮点数进行封送。你可以测试这个浮点数,或者是一个双浮点数。”引导Raymond Chen,现在,我们是吗?结束你们当时所说的,似乎我应该传递
float[,]
double[,]
元素,而不忘记转置原始数组。我会试试看,让你知道事情的进展。不要通过数组