C#与Fortran混合编程本地调用Fortran动态链接库.docx
《C#与Fortran混合编程本地调用Fortran动态链接库.docx》由会员分享,可在线阅读,更多相关《C#与Fortran混合编程本地调用Fortran动态链接库.docx(11页珍藏版)》请在冰豆网上搜索。
![C#与Fortran混合编程本地调用Fortran动态链接库.docx](https://file1.bdocx.com/fileroot1/2023-2/1/8f31914b-d78a-4304-a533-6d3b4c55549b/8f31914b-d78a-4304-a533-6d3b4c55549b1.gif)
C#与Fortran混合编程本地调用Fortran动态链接库
C#与Fortran混合编程-本地调用Fortran动态链接库
Fortran是一门古老的语言,它是世界上最早出现的计算机高级程序设计语言,广泛应用于科学和工程计算领域。
FORTRAN语言以其特有的功能在数值、科学和工程计算领域发挥着重要作用。
然而Fortran程序本身不适合开发独立的应用程序,例如我们传统的桌面应用或者Web应用。
因此这里我们便想将C#与Fortran结合,C#借助Fortran可以实现精度更高,计算更快的程序,而Fortran通过C#,便也能够达到可视化设计。
一、基本思路
运用Fortran,编写动态链接库(DLL),在DLL中提供计算的函数接口,然后在C#中调用该DLL中计算部分的函数,实现计算过程。
这里需要注意的是,由于我们使用的是Fortran编译器,生成的DLL属于第三方非托管DLL,因此无法直接在程序中添加DLL的引用。
具体的做法将在后续部分说明。
二、编写Fortran程序,生成动态链接库文件
知道思路之后便开始正式的Coding。
首先新建一个空的FortranDynamic-linkLibrary项目。
在Intel(R)VisualFortran点击Library,选中右图的Dynamic-linkLibrary.然后点击OK.这时的项目如下所示:
点击SourcesFile文件夹,选择新建项。
添加一个新的Fortran文件
然后便开始Fortran代码的编写工作。
这里我们主要实现两个方法:
一个方法是求两个数相加之和,并返回结果。
另一个是输入一个数组,对这个数组进行排序,并找出最大值,最后返回排序后的结果,并返回最大值。
这里我们分别演示的是Fortran传出一个数和一个数组有何不同。
关于Fortran的基本语法不是本文的讨论范畴,请读者自行查阅资料。
下面给出的上述我们要实现的功能的具体Fortran代码:
DOUBLEPRECISIONFUNCTIONADD(A,B)
!
DEC$ATTRIBUTESDLLEXPORT:
:
ADD
!
DEC$ATTRIBUTESSTDCALL,ALIAS:
'Add':
:
ADD
DOUBLEPRECISION:
:
A,B
ADD=A+B
END
FUNCTIONSORTANDFINDMAX(ARRAY,LENGTH)
!
DEC$ATTRIBUTESDLLEXPORT:
:
SORTANDFINDMAX
!
DEC$ATTRIBUTESSTDCALL,ALIAS:
'Sortandfindmax':
:
SORTANDFINDMAX
DOUBLEPRECISION:
:
ARRAY(LENGTH)
INTEGER:
:
I,J
DOUBLEPRECISION:
:
SORTANDFINDMAX,TEMP
SORTANDFINDMAX=ARRAY
(1)
DOI=1,LENGTH-1
DOJ=I+1,LENGTH
IF(ARRAY(I).GT.ARRAY(J))THEN
TEMP=ARRAY(I)
ARRAY(I)=ARRAY(J)
ARRAY(J)=TEMP
SORTANDFINDMAX=ARRAY(J)
ENDIF
ENDDO
ENDDO
END
上面我们声明了两个Fortran函数,一个是计算两个数相加,一个是选择排序并找出最大值。
之后我们点击VisualStudio的BuildSolution.开始编译成DLL。
关于代码段解释:
!
DEC$ATTRIBUTESDLLEXPORT:
:
ADD
!
DEC$ATTRIBUTESSTDCALL,ALIAS:
'Add':
:
ADD
这两句代码很关键。
下面通过三个一致来简单的说一下以上代码段的意思和C#调用需要注意的问题。
1.函数名一致:
在Fortran编译器中默认的导出函数名全部是大写形式。
而在C#中调用FortranDll时必须指定函数名一致。
在Fortran方面解决的办法是:
使用ALIAS(别名)属性指定导出函数名。
例如对于下面的Fortran函数:
DOUBLEPRECISIONFUNCTIONADD(A,B)
!
DEC$ATTRIBUTESDLLEXPORT:
:
ADD
DOUBLEPRECISIONA,B
ADD=A+B
END
对应的C#声明为:
[DllImport("MathDll")]
privatestaticexterndoubleADD(doubleA,doubleB);
使用ALIAS修改后的定义如下:
DoublePrecisionFunctionADD(A,B)
!
DEC$ATTRIBUTESDLLEXPORT:
:
ADD
!
DEC$ATTRIBUTESALIAS:
'Add':
:
Add
DoublePrecisionA,B
Add=A+B
End
对应的C#声明为:
[DllImport("MathDll")]
privatestaticexterndoubleAdd(doubleA,doubleB);
而在C#中提供的解决方案是:
通过使用Dlllmport的EntryPoint属性指定导出的Fortran函数名。
例如:
DoublePrecisionFunctionADD(A,B)
!
DEC$ATTRIBUTESDLLEXPORT:
:
ADD
DOUBLEPRECISIONA,B
ADD=A+B
END
对应的C#声明为:
[DllImport("MathDll",EntryPoint="ADD")]
privatestaticexterndoublePlus(doubleA,doubleB);
此外,还可以使用.NETFramework提供的dumpbin.exe工具查看DLL导出的函数名称。
A.在开始菜单中打开MicrosoftVisualStudio2010/VisualStudioTools/VisualStudio2010命令提示。
B.在命令提示窗体中将路径指向编译生成.dll文件的路径,然后输入以下命令:
dumpbin/exportsFileName.dll
即可查看当前目录下FileName.dIl中导出的所有函数信息。
2.堆栈管理一致
堆栈管理约定包括:
在调用过程中子例程接受参数的数目和顺序,调用完成后由哪一方来清理堆栈等。
C#语言在windows平台上的调用模式默认为StdCall模式,既由被调用方清理堆栈。
而Fortran语言则默认由调用方清除。
因此必须统一调用双方的堆栈清除方式才能保证2种语言间的正常函数调用。
这一约定在Fortran语言或C#语言中均可以采取措施进行统一。
在Fortran语言中可以通过编译指令“!
DEC$”后的可选项“C”或“STDCALL”参数来实现:
A.
!
DEC$ATTRIBUTESSTDCALL:
:
Object
该语句语句中的STDCALL模式指定由被调用方清除堆栈(其中“Object”为变量名或函数名)。
B.
!
DEC$ATTRIBUTESC:
:
Object
该语句中的C模式声明由主调函数清除堆栈(但在传递数组和字符串参数时不能用此方法指定)。
如果在C#语言内做改动,则需要在DllImport属性中设置CallingConvention字段的值为Cdecl(表示由调用方清理堆栈)或StdCall(表示由被调用方清理堆栈)。
[DllImport("FileName.dll",CallingConvention=CallingConvention.StdCall)]
[DllImport("FileName.dll",CallingConvention=CallingConvention.Cdecl)]
只有当Fortran程序和C#程序的堆栈管理一致时,才能保证正常的调用。
3.参数类型保持一致
在Fortran中常用的数据参数类型有:
REAL:
表示浮点数据类型,即小数,等价于C#的float,
INTEGER:
表示整数类型,相当于C#的int数据类型
DOUBLE PRECISION:
表示双精度数据类型,相当于C#的double数据类型。
在C#调用FortranDLL是必须保证参数的一致性,例如在Fortran中变量定义的是REAL类型,而我们传入的是Double,那么就会出现计算错误。
三、编写C#代码调用FortranDLL
C#调用的Fortran的过程很简单,只需要注意上述说的几个问题即可。
这里我们先新建一个控制台应用程序:
然后将我们编译的Fortran项目所生成的DLL拷贝到控制台应用程序的Debug文件夹下。
接着我们添加一个类:
FortranMethod.cs
该类用来调用FortranDLL。
代码如下:
usingSystem;
usingSystem.Text;
usingSystem.Runtime.InteropServices;
namespaceMixedProgram
{
publicstaticclassFortranMethod
{
[DllImport("TestDll.dll",CallingConvention=CallingConvention.Cdecl)]
publicstaticexterndoubleAdd(doublea,doubleb);
[DllImport("TestDll.dll",CallingConvention=CallingConvention.Cdecl)]
publicstaticexterndoubleSortandfindmax(double[]array,intlength);
}
}
关于C#调用注意的事项在上面已说明,在此不再讨论。
然后在Main函数中测试我们的FortranDLL。
示例代码如下:
usingSystem;
usingSystem.Collections.Generic;
usingSystem.Text;
namespaceMixedProgram
{
classProgram
{
staticvoidMain(string[]args)
{
Console.WriteLine("请输入两个数相加:
");
doublenum1=Convert.ToDouble(Console.ReadLine());
doublenum2=Convert.ToDouble(Console.ReadLine());
Console.WriteLine("输入的两个数是:
"+num1+","+num2);
doublesum=FortranMethod.Add(num1,num2);
Console.WriteLine("求和结果是:
"+sum);
double[]Array={1,5,2,4,3,7,6};
Console.WriteLine("初始数组:
");
for(inti=0;iConsole.Write(Array[i]+"");
doubleb=FortranMethod.Sortandfindmax(Array,Array.Length);
Console.WriteLine("\n"+"排序后:
");
for(inti=0;iConsole.Write(Array[i]+"");
Console.WriteLine("\n"+"最大值为:
");
Console.WriteLine(b);
Console.ReadKey();
}
}
}
到此为止,所以的工作已经完成,下面看一下结果:
到此为止,C#与Fortran编程的小示例就已经完成了。
总结:
本文主要演示了如何使用C#调用Fortran的DLL来实现相关的计算工作。
并主要讲了C#调用时应该注意的事项。
在工程计算中如果对于精度要求较高,计算较复杂时,我们便可以考虑通过C#与Fortran的混合编程来达到所需的要求。
本文是基于本地调用FortranDLL,下一篇将讲解基于Web调用FortranDLL.