键入编组以从C#调用fortran子例程

我正在尝试使用P / invoke从C#代码调用FORTRAN77子例程 – 如果您感兴趣,我正在尝试包装ARPACK库提供的一些function( http://www.caam.rice。教育/软件/ 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) 

我在这里找到了一些有价值的信息: 我应该为Fortran中的字符类型做什么? ,我暗示(我可能错了)我应该使用:

  • [MarshalAs(UnmanagedType.I4)] int传入整数
  • [MarshalAs(UnmanagedType.LPArray)] byte[]传入字符串

但是,我完全不知道如何处理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 ); 

我在这里做了几件事:

  • 传递作为UnmanagedType.I4编组的int对象以匹配FORTRAN integer对象
  • 传递float[,]对象的大小(m,n)并编组为UnmanagedType.LPArray以匹配FORTRAN Real(n,m)对象
  • 将获得的byte[]对象作为UnmanagedType.LPArray编组,以匹配FORTRAN Character*n对象。 byte[]对象的计算方法如下:

     System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding(); byte[] which = encoding.GetBytes(myString); 
  • 传递一个int对象BY VALUE并编组为UnmanagedType.I4以指示字符串的长度。 请注意,我试图将该参数放在字符串之后以及参数列表的末尾。

这是我最好的镜头 – 无论是这个还是我试过的所有其他事情都没有。 该方法将执行,但它超快速退出(当它应该进行一些非常严格的计算时)。 而且,我的3个数组被转换成奇怪的单维数组。 这是调试器给我的:

在此处输入图像描述

任何的想法?

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

这是一个工作示例,您可以使用有关它如何工作的许多想法。

原始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 

具有实数和整数forms的各种标量和数组值。 例如, DROP1是输入1Darrays。 PARRAY将2Darrays输出为1Darrays。 BLKL是输入浮点数。

注意!DEC$ ATTRIBUTES VALUE装饰,以避免将所有内容声明为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); } 

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

1)引用维基百科 :

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

所以将它作为一个浮标编组。 你可以测试那个,它可以是浮点数也可以是双精度数。

2)引用此Fortran 77教程 :

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

通过引用传递每个参数。