[转载:]C#与Fortran混合编程之本地调用Fortran动态链接库
前言
C#发展到现在,已是一门相当完善的语言,他基于C语言风格,演化于C++。并依靠强大的.NET底层框架。C#可以用来快速构建桌面及Web应用。然而在我们的实际工作中,尽管C#已经非常完善,但还是不能完成我们所有的工作。在很多工程计算中,C#语言的计算速度,精度,以及执行效率相对来说都达不到项目的要求。因此我们便考虑是否有一种方式将我们的工程计算部分和我们的项目分开,将计算部分用另一种执行更快,精度更高的语言来编写,然后在C#中调用,最后完成我们的工作。答案是肯定的。
Fortran是一门古老的语言,它是世界上最早出现的计算机高级程序设计语言,广泛应用于科学和工程计算领域。FORTRAN语言以其特有的功能在数值、科学和工程计算领域发挥着重要作用。然而Fortran程序本身不适合开发独立的应用程序,例如我们传统的桌面应用或者Web应用。因此这里我们便想将C#与Fortran结合,C#借助Fortran可以实现精度更高,计算更快的程序,而Fortran通过C#,便也能够达到可视化设计。
一、基本思路
运用Fortran,编写动态链接库(DLL),在DLL中提供计算的函数接口,然后在C#中调用该DLL中计算部分的函数,实现计算过程。
这里需要注意的是,由于我们使用的是Fortran编译器,生成的DLL属于第三方非托管DLL,因此无法直接在程序中添加DLL的引用。具体的做法将在后续部分说明。
二、编写Fortran程序,生成动态链接库文件
知道思路之后便开始正式的Coding。首先新建一个空的Fortran Dynamic-link Library项目。
在Intel(R) Visual Fortran点击Library,选中右图的Dynamic-link Library.然后点击OK.这时的项目如下所示:
点击Sources File文件夹,选择新建项。
添加一个新的Fortran文件
然后便开始Fortran代码的编写工作。这里我们主要实现两个方法:
一个方法是求两个数相加之和,并返回结果。
另一个是输入一个数组,对这个数组进行排序,并找出最大值,最后返回排序后的结果,并返回最大值。
这里我们分别演示的是Fortran传出一个数和一个数组有何不同。
关于Fortran的基本语法不是本文的讨论范畴,请读者自行查阅资料。
下面给出的上述我们要实现的功能的具体Fortran代码:
DOUBLE PRECISION FUNCTION ADD(A,B)
!DEC$ ATTRIBUTES DLLEXPORT::ADD
!DEC$ ATTRIBUTES STDCALL,ALIAS:'Add'::ADD
DOUBLE PRECISION:: A,B
ADD=A+B
END FUNCTION SORTANDFINDMAX(ARRAY,LENGTH)
!DEC$ ATTRIBUTES DLLEXPORT::SORTANDFINDMAX
!DEC$ ATTRIBUTES STDCALL,ALIAS:'Sortandfindmax'::SORTANDFINDMAX
DOUBLE PRECISION ::ARRAY(LENGTH)
INTEGER::I,J
DOUBLE PRECISION::SORTANDFINDMAX,TEMP
SORTANDFINDMAX=ARRAY()
DO I=,LENGTH-
DO J=I+,LENGTH
IF(ARRAY(I).GT.ARRAY(J)) THEN
TEMP=ARRAY(I)
ARRAY(I)=ARRAY(J)
ARRAY(J)=TEMP
SORTANDFINDMAX=ARRAY(J)
END IF
END DO
END DO
END
上面我们声明了两个Fortran函数,一个是计算两个数相加,一个是选择排序并找出最大值。
之后我们点击Visual Studio的Build Solution.开始编译成DLL。
关于代码段解释:
!DEC$ ATTRIBUTES DLLEXPORT::ADD
!DEC$ ATTRIBUTES STDCALL,ALIAS:'Add'::ADD
这两句代码很关键。下面通过三个一致来简单的说一下以上代码段的意思和C#调用需要注意的问题。
1.函数名一致:
在Fortran编译器中默认的导出函数名全部是大写形式。而在C#中调用Fortran Dll时必须指定函数名一致。在Fortran方面解决的办法是:使用ALIAS(别名)属性指定导出函数名。
例如对于下面的Fortran函数:
DOUBLE PRECISION FUNCTION ADD(A, B) !DEC$ ATTRIBUTES DLLEXPORT:: ADD DOUBLE PRECISION A,B ADD =A+B END
对应的C#声明为:
[DllImport("MathDll")] private static extern double ADD (double A, double B);
使用ALIAS修改后的定义如下:
Double Precision Function ADD (A, B) !DEC$ ATTRIBUTES DLLEXPORT:: ADD !DEC$ ATTRIBUTES ALIAS:'Add' :: Add Double Precision A,B Add =A+B End
对应的C#声明为:
[DllImport("MathDll")] private static extern double Add (double A, double B);
而在C#中提供的解决方案是:通过使用Dlllmport的EntryPoint属性指定导出的Fortran函数名。
例如:
Double Precision Function ADD(A, B) !DEC$ ATTRIBUTES DLLEXPORT:: ADD DOUBLE PRECISION A,B ADD =A+B END
对应的C#声明为:
[DllImport("MathDll",EntryPoint = " ADD ")] private static extern double Plus(double A, double B);
此外,还可以使用 .NET Framework提供的dumpbin.exe工具查看DLL导出的函数名称。
A. 在开始菜单中打开Microsoft Visual Studio 2010/Visual Studio Tools/ Visual Studio 2010 命令提示。
B. 在命令提示窗体中将路径指向编译生成.dll文件的路径,然后输入以下命令:
dumpbin /exports FileName.dll
即可查看当前目录下FileName.dIl中导出的所有函数信息。
2. 堆栈管理一致
堆栈管理约定包括:在调用过程中子例程接受参数的数目和顺序,调用完成后由哪一方来清理堆栈等。C#语言在windows平台上的调用模式默认为StdCall模式,既由被调用方清理堆栈。而Fortran语言则默认由调用方清除。因此必须统一调用双方的堆栈清除方式才能保证2种语言间的正常函数调用。这一约定在Fortran语言或C#语言中均可以采取措施进行统一。
在Fortran语言中可以通过编译指令“!DEC$”后的可选项“C”或“STDCALL”参数来实现:
A.
!DEC$ ATTRIBUTES STDCALL ::Object
该语句语句中的STDCALL模式指定由被调用方清除堆栈(其中“Object”为变量名或函数名)。
B.
!DEC$ ATTRIBUTES C :: 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#调用Fortran DLL是必须保证参数的一致性,例如在Fortran中变量定义的是REAL类型,而我们传入的是Double,那么就会出现计算错误。
三、编写C#代码调用Fortran DLL
C#调用的Fortran的过程很简单,只需要注意上述说的几个问题即可。
这里我们先新建一个控制台应用程序:
然后将我们编译的Fortran项目所生成的DLL拷贝到控制台应用程序的Debug文件夹下。
接着我们添加一个类:FortranMethod.cs
该类用来调用Fortran DLL。
代码如下:
using System;
using System.Text;
using System.Runtime.InteropServices; namespace MixedProgram
{
public static class FortranMethod
{
[DllImport("TestDll.dll",CallingConvention = CallingConvention.Cdecl)]
public static extern double Add(double a, double b); [DllImport("TestDll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern double Sortandfindmax(double[] array, int length);
}
}
关于C#调用注意的事项在上面已说明,在此不再讨论。
然后在Main函数中测试我们的Fortran DLL。
示例代码如下:
using System;
using System.Collections.Generic;
using System.Text; namespace MixedProgram
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("请输入两个数相加:");
double num1=Convert.ToDouble(Console.ReadLine()); double num2 = Convert.ToDouble(Console.ReadLine());
Console.WriteLine("输入的两个数是:" + num1 + " ," + num2);
double sum = FortranMethod.Add(num1,num2);
Console.WriteLine("求和结果是:"+sum); double[] Array = { ,,,,,,};
Console.WriteLine("初始数组:");
for (int i = ; i < Array.Length; i++)
Console.Write(Array[i]+" "); double b=FortranMethod.Sortandfindmax(Array, Array.Length);
Console.WriteLine("\n"+"排序后:"); for (int i = ; i < Array.Length; i++)
Console.Write(Array[i]+" "); Console.WriteLine("\n"+"最大值为:");
Console.WriteLine(b); Console.ReadKey();
}
}
}
到此为止,所以的工作已经完成,下面看一下结果:
到此为止,C#与Fortran编程的小示例就已经完成了。
总结:
本文主要演示了如何使用C#调用Fortran的DLL来实现相关的计算工作。并主要讲了C#调用时应该注意的事项。在工程计算中如果对于精度要求较高,计算较复杂时,我们便可以考虑通过C#与Fortran的混合编程来达到所需的要求。本文是基于本地调用Fortran DLL,下一篇将讲解基于Web调用Fortran DLL.
关于C#与Fortran混合编程还可参加这篇文章:http://www.iepi.com.cn/BBS_CN/forum.php?mod=viewthread&tid=62&extra=page%3D1
本文转自:http://www.cnblogs.com/potential/archive/2012/11/05/2755899.html
[转载:]C#与Fortran混合编程之本地调用Fortran动态链接库的更多相关文章
- matlab和fortran混合编程
matlab2016b+vs2010+ivf2013+f90 其实默认是f77语法,但通过配置可以改变为自由格式. 默认只能f77代码,怎样修改: https://ww2.mathworks.cn/m ...
- 【调试经验】C++和C的混合编程以及库调用
问题背景 这两天在移植一个开源的库,偏底层的那种,所以对架构有一些依赖.源码的编译是通过Makefile来构建,怎奈公司的架构用的是CMAKE,所以就在开源的顶层和子目录分别构建了CMakeList, ...
- Matlab与C++混合编程 1--在C++中调用自己写的matlab函数
在Visual Studio中使用C++中调用MATLAB程序 在matlab中可以通过mbuild工具将.m文件编译成dll文件供外部的C++程序调用,这样就可以实现matlab和C++混合编程的目 ...
- python c++ 混合编程中python调用c++string返回类型的函数,python中返回为数字的解决办法
本随笔解决 Python使用ctypes 调用c++dll 字符串返回类型函数,在python中显示为数字:原文解决方案见so: https://stackoverflow.com/questions ...
- C#调用Fortran生成的DLL的方法报内存不足
最近在研究一个程序,公司给的,程序是VB写的,程序里面还有一个计算的模型,用Fortran语言写的. 在调试到这个模型里面的方法时报错,说是内存不足,于是就在网上查找方法,看了两篇博客之后问题解决了. ...
- Fortran与C混合编程(转自Ubuntu)
Fortran与C混合编程 由于 GNU 的 Fortran 和 C 语言二者的函数彼此可以直接相互调用,所以混合编程可以非常容易地实现.只要你足够仔细,确保函数调用时传递的参数类型正确,函数就可以在 ...
- Fortran与C/C++混合编程示例
以下例子均来自网络,只是稍作了编辑,方便今后查阅. 子目录 (一) Fortran调用C语言 (二) C语言调用Fortran (三) C++ 调用Fortran (四) Fortran 调用 C++ ...
- Python调用C/Fortran混合的动态链接库--上篇
内容描述: 在32位或64位的windows或GNU/Linux系统下利用Python的ctypes和numpy模块调用C/Fortran混合编程的有限元数值计算程序 操作系统及编译环境: 32bit ...
- matlab和c++混合编程---matlab和vs的环境配置问题及方法和步骤(转载)
matlab和c++混合编程---方法和步骤 matlab和c++混合编程---matlab和vs的环境配置问题 摘要:Matlab具有很强的数值计算和分析等能力,而C/C++是目前最为流行的高级程序 ...
随机推荐
- 网易Ubuntu镜像使用帮助(ubuntu15.10 修改源)
原文位置 以Wily(15.10)为例, 编辑/etc/apt/sources.list文件, 在文件最前面添加以下条目(操作前请做好相应备份) deb http://mirrors.163.com/ ...
- CSS中的class与id区别及用法
转自http://www.divcss5.com/rumen/r3.shtml及http://www.jb51.net/css/35927.html 我们平常在用DIV CSS制作Xhtml网页页面时 ...
- JVM 参数分配
http://stackoverflow.com/questions/41216388/java-jvm-parameter-xms-doesnt-take-effect-immediately It ...
- C#模拟键盘事件
public partial class Form1 : Form { public Form1() { InitializeComponent(); } [DllImport("USER3 ...
- php excel读取
当然首先要判断是否有文件和文件类型,接着把文件保存到某个路径中 /** * 读取excel数据 * @author Red * @date * @param $filename 文件所在路径+文件名 ...
- appium 环境搭建 java
1 安装node.js 1.1 安装node.js http://nodejs.cn/download/ 1.2.下载后直接点击exe,按照提示一步一步的安装 1.3 安装成功后,运行cmd,输入no ...
- java关键字:synchronized
JAVA 如何共享资源 关于synchronized函数: java具有内置机制,可防止某种资源(此处指的是对象的内存内容)冲突.由于你通常会将某class的数据元素声明为private,并且只经由其 ...
- Oracle10g RAC关闭及启动步骤
情况1:需要关闭DB(所有实例),OS及Server 停RAC的顺序是: 1)数据库 -〉 2)ASM -〉 3)CRS a.首先停止Oracle10g环境 $ lsnrctl stop (每个节 ...
- 【mysql】Infobright和mysql数据入库性能测试
产生测试文件 测试文件部分内容如下: 产生测试文件代码: package foo; import java.io.File; import java.io.FileWriter; import jav ...
- 黄聪:C#带cookie模拟登录百度
#region 同步通过POST方式发送数据 /// <summary> /// 通过POST方式发送数据 /// </summary> /// <param name= ...