Fortran Dll导入

时间:2014-03-02 22:09:23

标签: c# fortran pinvoke dllimport

Fortran中的代码Robert L. Parker and Philip B. Stark

FORTRAN

      subroutine bv(key, m, n, a, b, bl, bu, x, w, act, zz, istate,  loopA)
      implicit double precision (a-h, o-z)
!      x  is an unknown n-vector
!      a  is a given m by n matrix
!      b  is a given  m-vector 
!      bl is a given n-vector of lower bounds on the components of x.
!      bu is a given n-vector of upper bounds on the components of x.
!      key = 0
!   ---Output parameters:
!      x   the solution vector.
!      w(1)    the minimum 2-norm || a.x-b ||.
!  istate  vector indicating which components of  x  are active 
!  loopA   number of iterations taken in the main loop, Loop A.
!  ---Working  arrays:
!  w      dimension n.               act      dimension m*(mm+2). mm=min(m,n).
!  zz     dimension m.               istate   dimension n+1.

我试图从c#中的dll调用该函数,如:

C#

class Program
{
    [DllImport("bv.dll", CallingConvention = CallingConvention.StdCall )]
    public static extern void bvls(
         int key, //key = 0, the subroutine solves the problem from scratch. If key > 0 the routine initializes using the user's guess about which components of  x  are `active'
         int m,
         int n,
        double[] a, //  m by n matrix
        double[] b, //  m-vector 
        double[] bl, //n-vector of lower bounds on the components of x.
        double[] bu, //n-vector of upper bounds on the components of x.
        ref double[] x, //unknown n-vector
        //Working  arrays:
        ref double[] w,  //dimension n
        double[] act, //dimension m*(mm+2). mm=min(m,n).
         double[] zz, //dimension m
        ref double[] istate, //dimension n+1.
        ref int loopA //   number of iterations taken in the main loop, Loop A.
        );


    static void Main(string[] args)
    {
        double[] a = new double[3 * 3] { //M*N
                  1.0, 10.0, 10.0,
                  2.0, 18.0, 16.0,
                  1.8, 69.0, 16.0
        };
        double[] b = new double[3]  {  //LDB*NRHS
             4.3, 6.8, 1.0,
        };
        double[] bl = new double[3];
        double[] bu = new double[3];
        double[] x = new double[3];
        double[] w = new double[3];
        double[] act = new double[3* 5];  //dimension m*(mm+2). mm=min(m,n).
        double[] zz = new double[3];
        double[] istate = new double[4];
        int loopA =0;
        Program.bv(0, 3, 3, a, b, bl, bu, ref x, ref w, act, zz, ref istate, ref loopA);
        for (int j = 0; j < 3; j++)
            Console.Write(" \t" + x[j]);

    }
}

然而,在执行代码时我得到了

EntryPointNotFoundException: Entry point was not found. in 'bv' on file 'bv.dll'.
    myProject.Program.bv(Int32 key, Int32 m, Int32 n, Double[] a, Double[] b, Double[] bl, Double[] bu, Double[]& x, Double[]& w, Double[] act, Double[] zz, Double[]& istate, Int32& loopA)

基本上我有2个问题, 如何让这个工作?和其他问题,我定义函数的方式是否正确

 [DllImport("bv.dll", CallingConvention = CallingConvention.StdCall )]
            public static extern void bvls(...)

基于fortran代码的例行信息?

使用dependency walker时,我得到: enter image description here

我怀疑dll不正确并且没有例程,有没有办法检查dll是否以正确的方式生成?

更新

尝试ILSpy之后,我得到了以下似乎不正确的内容,请你建议如何正确生成dll文件?,ILSpy是否告诉bvlsFortran是我应该使用的函数?我试了但是无法让它发挥作用 enter image description here

enter image description here

3 个答案:

答案 0 :(得分:2)

FORTRAN

中试试
MODULE CALCBV

    INTEGER, PARAMETER :: sp = SELECTED_REAL_KIND(p=6,r=37)     ! IEEE Single Precision (32-bit)
    INTEGER, PARAMETER :: dp = SELECTED_REAL_KIND(p=15,r=307)   ! IEEE Double Precision (64-bit)       

CONTAINS

    subroutine bv(key, m, n, a, b, bl, bu, x, w, act, zz, istate,  loopA)
    IMPLICIT NONE
    !DEC$ ATTRIBUTES DLLEXPORT :: bv
    !DEC$ ATTRIBUTES ALIAS:'BV' :: bv
    !DEC$ ATTRIBUTES VALUE :: key, m, n

    INTEGER, INTENT(IN)   :: key, m, n
    REAL(dp), INTENT(IN)  :: a(m,n), b(m), bl(n), bu(n)
    REAL(dp), INTENT(OUT) :: x(n), w(n)
    REAL(dp), INTENT(IN)  :: act(m,MIN(m,n)+2), zz(m)
    INTEGER, INTENT(OUT)  :: istate(n+1)
    INTEGER, INTENT(OUT)  :: loopA

    ! DO CALC HERE

    end subroutine

END MODULE

然后从C#拨打:

[DllImport("bv.dll", CallingConvention=CallingConvention.Cdecl, EntryPoint="BV")]
static extern void bvls(
    int key, //key = 0, the subroutine solves the problem from scratch. If key > 0 the routine initializes using the user's guess about which components of  x  are `active'
    int m,
    int n,
    double[] a, //  m by n matrix
    double[] b, //  m-vector 
    double[] bl, //n-vector of lower bounds on the components of x.
    double[] bu, //n-vector of upper bounds on the components of x.
    double[] x, //unknown n-vector
    //Working  arrays:
    double[] w,  //dimension n
    double[] act, //dimension m*(mm+2). mm=min(m,n).
    double[] zz, //dimension m
    int[] istate, //dimension n+1.
    ref int loopA //   number of iterations taken in the main loop, Loop A.
    );

// Test code
static void BVTEST()
{
    int key=0, n=2, m=3;
    double[] a= { 1.0, 2.0, 3.0, 4.0, 5.0 };
    double[] b= { 10.0, 20.0, 30.0 };
    double[] bl= { 0.0, 1.0 };
    double[] bu= { 1.0, 2.0 };
    double[] x=new double[n];
    double[] w=new double[n];
    double[] act=new double[m*Math.Min(m, n)+2];
    double[] zz=new double[m];
    int[] istate=new int[n+1];
    int loopA = 0;
    // Call Fortran .dll
    bvls(key, m, n, a, b, bl, bu, x, w, act, zz, istate, ref loopA);
}

ScreenShot

记住数组已经是引用类型(默认值),因此它们不需要ref个关键字。输出值需要像loopA一样,但是通过值参数传递需要VALUE属性声明,以避免使用ref传递它们,就像key一样,{{1 },m。您可能需要将n的大小修改为更大的值,因为在此之后参数中会出现内存损坏。

这篇文章应该让你朝着正确的方向前进。请记住,始终将act与FORTRAN .dll一起使用,始终使用Cdecl声明。编译为implicit nonex86,不要使用Win32。使用AnyCPU attirbute声明您的导出以显示。

DependecyWalker

答案 1 :(得分:1)

查看您的最新更新,您的Fortran编译器似乎已发出.net程序集。你应该添加它作为参考,并忘记p / invoke的所有内容。像任何其他人一样使用.net程序集。


这是原始答案,评论p / invoke

关于函数名称的问题似乎有些混乱。是bv还是bvls?在任何情况下,Fortran编译器都可以装饰它。使用Dependency Walker查找其名称。好的,我看到你试过了。似乎您的DLL编译未能导出该函数。您需要了解特定编译器如何标记导出函数。

调用约定可以是stdcall或cdecl。这取决于DLL的编译方式和编译器的使用方式。 IIRC,Windows上的大多数Fortran编译器都将使用stdcall。检查编译器文档。

您使用ref表示几个数组参数。这是不正确的。从数组参数中删除ref。数组被编组为指向第一个元素的指针。您永远不会通过ref将数组编组为本机,因为本机代码无法创建托管数组。

istate参数的类型为int[]。除此之外,我认为你的类型是正确的。

我没有检查你如何准备参数。值得在适当的时候仔细检查。你肯定需要确保你尊重Fortran的主要存储空间。

答案 2 :(得分:1)

我首先尝试为一个小例子生成一个DLL,让它运行,而不是为你的那个大的FORTRAN代码。

过去我在创建FORTRAN DLL并使用C#代码调用它时遇到了一些问题。

你要做的第一件事就是让自己为FORTRAN代码编写一个很酷的编译器,我在那个时候使用Open Watcom,因为它有一个易于使用的IDE。我从中创造了我的dll。

这是我尝试过的第一个FORTRAN代码:

*$pragma aux DON "DON" export parm(value*8, reference, reference)


  SUBROUTINE DON(DAA,DBB,DCC)
  REAL*8, DAA,DBB,DCC
  DBB=DAA+1
  DCC=DBB+1 
  RETURN
  END

$ pragma指令仅供Open Watcom编译器使用。所以如果你要使用不同的编译器,请不要介意。

这是一个简单的子程序供您测试。如果你可以为你做这项工作,你将成为制造你想要的那个巨大的一半。 : - )

我曾经称之为的C#代码就是这里的代码:

[DllImport("Lks.dll",
        CharSet = CharSet.Auto,
        CallingConvention = CallingConvention.StdCall)]
    public static extern void DON(
        [MarshalAs(UnmanagedType.R8)]     double DAA,
        [MarshalAs(UnmanagedType.R8)] ref double DBB,
        [MarshalAs(UnmanagedType.R8)] ref double DCC
        );

    static unsafe void Main(string[] args)
    {
        //double TIME = 100.0;
        double DAA = 5.5;
        double DBB = 7;
        double DCC = 9;
        //START( ENERIN, VAL1);
        DON(DAA, DBB, DCC);

        Console.Write("val1 = " + DAA);
        Console.Write("val2 = " + DCC);
        Debug.WriteLine("VAR = " + DBB.ToString());
        Console.Write("Press any key to exit");
        Console.ReadKey(false);
    }

因此,对于您的情况,我认为您可以做的几乎相同,但您需要注意在MarshalAs上获取正确的类型。

这对我有用,我希望它能指出你正确的方向。您可以从此处获取Open Watcom(http://www.openwatcom.org/

我是个菜鸟,这是我的第一个答案,所以如果我无法帮助,请不要介意我。

-Roiw