[转载:]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代码:

 1 DOUBLE PRECISION FUNCTION ADD(A,B)
 2 !DEC$ ATTRIBUTES DLLEXPORT::ADD
 3 !DEC$ ATTRIBUTES STDCALL,ALIAS:‘Add‘::ADD
 4     DOUBLE PRECISION:: A,B
 5     ADD=A+B
 6 END
 7
 8 FUNCTION SORTANDFINDMAX(ARRAY,LENGTH)
 9 !DEC$ ATTRIBUTES DLLEXPORT::SORTANDFINDMAX
10 !DEC$ ATTRIBUTES STDCALL,ALIAS:‘Sortandfindmax‘::SORTANDFINDMAX
11 DOUBLE PRECISION ::ARRAY(LENGTH)
12 INTEGER::I,J
13 DOUBLE PRECISION::SORTANDFINDMAX,TEMP
14 SORTANDFINDMAX=ARRAY(1)
15 DO I=1,LENGTH-1
16     DO J=I+1,LENGTH
17         IF(ARRAY(I).GT.ARRAY(J)) THEN
18             TEMP=ARRAY(I)
19             ARRAY(I)=ARRAY(J)
20             ARRAY(J)=TEMP
21             SORTANDFINDMAX=ARRAY(J)
22             END IF
23     END DO
24     END DO
25 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函数:

1 DOUBLE PRECISION FUNCTION ADD(A, B)
2
3 !DEC$ ATTRIBUTES DLLEXPORT:: ADD
4
5 DOUBLE PRECISION A,B
6
7 ADD =A+B
8
9 END

对应的C#声明为:

[DllImport("MathDll")]

private static extern double ADD (double A, double B);

使用ALIAS修改后的定义如下:

 1 Double Precision Function ADD (A, B)
 2
 3 !DEC$ ATTRIBUTES DLLEXPORT:: ADD
 4
 5 !DEC$ ATTRIBUTES ALIAS:‘Add‘ :: Add
 6
 7 Double Precision A,B
 8
 9 Add =A+B
10
11 End

对应的C#声明为:

[DllImport("MathDll")]

private static extern double Add (double A, double B);

而在C#中提供的解决方案是:通过使用Dlllmport的EntryPoint属性指定导出的Fortran函数名。

例如:

1 Double Precision Function ADD(A, B)
2
3 !DEC$ ATTRIBUTES DLLEXPORT:: ADD
4
5 DOUBLE PRECISION A,B
6
7 ADD =A+B
8
9 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。

代码如下:

 1 using System;
 2 using System.Text;
 3 using System.Runtime.InteropServices;
 4
 5 namespace MixedProgram
 6 {
 7    public static class FortranMethod
 8     {
 9        [DllImport("TestDll.dll",CallingConvention = CallingConvention.Cdecl)]
10        public static extern double Add(double a, double b);
11
12        [DllImport("TestDll.dll", CallingConvention = CallingConvention.Cdecl)]
13        public static extern double Sortandfindmax(double[] array, int length);
14     }
15 }

关于C#调用注意的事项在上面已说明,在此不再讨论。

然后在Main函数中测试我们的Fortran DLL。

示例代码如下:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Text;
 4
 5 namespace MixedProgram
 6 {
 7     class Program
 8     {
 9         static void Main(string[] args)
10         {
11             Console.WriteLine("请输入两个数相加:");
12             double num1=Convert.ToDouble(Console.ReadLine());
13
14             double num2 = Convert.ToDouble(Console.ReadLine());
15             Console.WriteLine("输入的两个数是:" + num1 + " ," + num2);
16             double sum = FortranMethod.Add(num1,num2);
17             Console.WriteLine("求和结果是:"+sum);
18
19             double[] Array = { 1,5,2,4,3,7,6};
20             Console.WriteLine("初始数组:");
21             for (int i = 0; i < Array.Length; i++)
22                 Console.Write(Array[i]+" ");
23
24             double b=FortranMethod.Sortandfindmax(Array, Array.Length);
25             Console.WriteLine("\n"+"排序后:");
26
27             for (int i = 0; i < Array.Length; i++)
28                 Console.Write(Array[i]+" ");
29
30             Console.WriteLine("\n"+"最大值为:");
31             Console.WriteLine(b);
32
33             Console.ReadKey();
34         }
35     }
36 }

到此为止,所以的工作已经完成,下面看一下结果:

到此为止,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

时间: 2025-01-02 09:36:56

[转载:]C#与Fortran混合编程之本地调用Fortran动态链接库的相关文章

【调试经验】C++和C的混合编程以及库调用

问题背景 这两天在移植一个开源的库,偏底层的那种,所以对架构有一些依赖.源码的编译是通过Makefile来构建,怎奈公司的架构用的是CMAKE,所以就在开源的顶层和子目录分别构建了CMakeList,但奇怪的是CMAKE产生顶层的Makefile在编译阶段并没有执行,试了一些方法后还是不行,迫于时间压力,只能把项目中用到的源文件提取出来,放到一个文件夹内,单独写CMakeList,最终产生了一个静态库.但是在链接的时候产生了错误,相关函数没定义,又是熟悉的undefined reference

C#与MATLAB混合编程--DLL动态调用

一.MATLAB生成C#可调用的DLL MATLAB生成C#可调用的DLL可以使用MATLAB提供的deploytool工具.在命令行中敲入deploytool就可以打开这个工具.MATLAB编译器可能会需要提前配置. 打开工具对话框后填写项目名称.路径,并在type选项中选择.NET Assembly,这个选项能够生成用于C#的DLL. 完成后再左侧的.NET Assembly选项卡中点击编辑Class名称,然后在其下添加响应的m文件,如果m文件引用了其它的m文件,可以将所有关联的文件一起加入

C#调用Fortran生成的DLL的方法报内存不足

最近在研究一个程序,公司给的,程序是VB写的,程序里面还有一个计算的模型,用Fortran语言写的. 在调试到这个模型里面的方法时报错,说是内存不足,于是就在网上查找方法,看了两篇博客之后问题解决了. C#与Fortran混合编程之本地调用Fortran动态链接库 C#与Fortran混合编程:http://www.iepi.com.cn/BBS_CN/forum.php?mod=viewthread&tid=62&extra=page%3D1 原因是堆栈释放的问题,我用C#做的测试,和V

FORTRAN &amp; MATLAB 混合编程

[email protected] 第一部分:Fortran调用Matlab引擎 1  什么是Matlab引擎 所谓Matlab引擎(engine),是指一组Matlab提供的接口函数,支持C/C++.Fortran等语言,通过这些接口函数,用户可以在其它编程环境中实现对Matlab的控制.可以主要功能有: ★ 打开/关闭一个Matlab对话: ★ 向Matlab环境发送命令字符串: ★ 从Matlab环境中读取数据: ★ 向Matlab环境中写入数据. 与其它各种接口相比,引擎所提供的Matl

Python调用C/Fortran混合的动态链接库--上篇

内容描述: 在32位或64位的windows或GNU/Linux系统下利用Python的ctypes和numpy模块调用C/Fortran混合编程的有限元数值计算程序 操作系统及编译环境: 32bit Win7 + mingw32 + gcc-4.8 + msys-1.0 + python2.7 + openblas-0.2.8 64bit Ubuntu 1404 + gcc-4.8 + python2.7 + openblas-0.2.8 任务分解: 1.打包有限元数值程序,生成dll或so格

win64 qt与fortran (codeblocks) 混合编程

本教程主要解说用fortran生成dll供qt调用(win64) 本教程须要的软件及文件可从以下的连接下载: http://pan.baidu.com/s/1c04jziC fortran我用的软件是codeblocks.这是一款包括基于gfortran的开源软件. 1.先来说说codebolcks的环境配置: 先下载codeblocks(下面简称cb),这个没啥好说的,下载就是了. 1.1.codebolcks的汉化: 将下载的.mo文件放在:CodeBlocks\share\CodeBloc

用c/c++混合编程方式为ios/android实现一个自绘日期选择控件(一)

本文为原创,如有转载,请注明出处:http://www.cnblogs.com/jackybu 前言 章节: 1.需求描述以及c/c++实现日期和月历的基本操作 2.ios实现自绘日期选择控件 3.android实现自绘日期选择控件 目的: 通过一个相对复杂的自定义自绘控件来分享: 1.ios以及android自定义自绘控件的开发流程 2.objc与c/c++混合编程 3.android ndk的环境配置,android studio ndk的编译模式,swig在android ndk开发中的作

Qt Quick 之 QML 与 C++ 混合编程详解

Qt Quick 技术的引入,使得你能够快速构建 UI ,具有动画.各种绚丽效果的 UI 都不在话下.但它不是万能的,也有很多局限性,原来 Qt 的一些技术,比如低阶的网络编程如 QTcpSocket ,多线程,又如 XML 文档处理类库 QXmlStreamReader / QXmlStreamWriter 等等,在 QML 中要么不可用,要么用起来不方便,所以呢,很多时候我们是会基于这样的原则来混合使用 QML 和 C++: QML 构建界面, C++ 实现非界面的业务逻辑和复杂运算. 请给

C# 托管和非托管混合编程

在非托管模块中实现你比较重要的算法,然后通过 CLR 的平台互操作,来使托管代码调用它,这样程序仍然能够正常工作,但对非托管的本地代码进行反编译,就很困难. 最直接的实现托管与非托管编程的方法就是使用C++/CLI 介绍 项目存档一直是企业的采用的做法,而是事实证明他们也是对的!对于一个程序员,这是几千men-days的工作量.为什么不开发一小段代码去重新利用那段代码,项目. 现在提供了一个渐渐的转向C#的新技术: 使用托管与非托管的混合编程.这是一个可行的方案在top-down issue(f