Fortran中的代码Robert L. Parker and Philip B. Stark:
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调用该函数,如:
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时,我得到:
我怀疑dll不正确并且没有例程,有没有办法检查dll是否以正确的方式生成?
尝试ILSpy之后,我得到了以下似乎不正确的内容,请你建议如何正确生成dll文件?,ILSpy是否告诉bvlsFortran是我应该使用的函数?我试了但是无法让它发挥作用
答案 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);
}
记住数组已经是引用类型(默认值),因此它们不需要ref
个关键字。输出值需要像loopA
一样,但是通过值参数传递需要VALUE
属性声明,以避免使用ref
传递它们,就像key
一样,{{1 },m
。您可能需要将n
的大小修改为更大的值,因为在此之后参数中会出现内存损坏。
这篇文章应该让你朝着正确的方向前进。请记住,始终将act
与FORTRAN .dll一起使用,始终使用Cdecl
声明。编译为implicit none
和x86
,不要使用Win32
。使用AnyCPU
attirbute声明您的导出以显示。
答案 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