C# Fortran混合编程
基本目标 : 利用c#调用 fortran中的 souroutine
或者 function
方法。通过把fortran 代码编译成 .dll
(windows)/.so
(unix) 动态库,放到 c# 的执行路径下。再通过 c# 中的DllImport
方法导入库函数,实现在 c# 中调用 fortran 方法。
1. DllImport
方法
该方法 来自 System.Runtime.InteropServices , 是由.Net提供的方法。例如可以使用下面的代码调用user32.dll
中的MessageBox
方法。
using System; using System.Runtime.InteropServices; MessageBox (IntPtr.Zero,"Please do not press this again.", "Attention", 0); [DllImport("user32.dll")] static extern int MessageBox (IntPtr hWnd, string text, string caption, int type);
这里在c# 中声明方法的时候需要注意以下几点:
- 函数应该声明为 static, extern 方法
- 函数的参数类型需要注意,这里仅仅以fortran为例。下文介绍了几种常见参数传递的对应情况。
DllImport
方法有几个参数也需要注意:
- EntryPoint:这需要使用一些第三方工具从dll / so 文件中查找。win下使用vs 带的 dumpbin.exe方法,使用
dumpbin /EXPORTS target.dll
命令查看。linux 下使用nm -D target.so
查找定义的方法的entrypoint。 - CallingConvention: 主要使用
CallingConvention.Cdecl
或者CallingConvention.StdCall
, 具体差别可以google.
2. c# 方法 与 Fortran 方法 参数对应
这里以示例代码来说明
module one implicit none abstract interface subroutine Add(b, c, a) implicit none integer b real*8 a dimension a(*) real*8 c end subroutine end interface contains subroutine CallfuntoAdd(f, b, c, a) !DEC$ ATTRIBUTES ALIAS:'CallfuntoAdd' :: CallfuntoAdd !DEC$ ATTRIBUTES DLLEXPORT :: CallfuntoAdd !DEC$ ATTRIBUTES reference :: f, a, b, c implicit none real*8 c integer b real*8 a dimension a(*) procedure(Add)::f Call f(b, c, a) write(*,*) " c = ", c return end subroutine end module one
在上面的Fortran代码里定义了一个CallfuntoAdd(f, b, c, a)方法,可以通过!DEC$
被导出到 dll 文件中。这里f 是一个回调函数, b 是integer类型, c是real*8类型, a 是一个一维数组。
public class c1 { [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void AddFunc(ref int b, ref double c, [In] IntPtr a); public static void Add1(ref int b, ref double c, [In] IntPtr a) { double[] a1 = new double[b]; Marshal.Copy(a, a1, 0, b); c = 0; for (int i = 0; i < b; i++) { c += a1[i]; } } [DllImport("mod1.dll", EntryPoint = "__one_MOD_callfuntoadd", CallingConvention = CallingConvention.Cdecl)] public static extern void CallfuntoAdd([MarshalAs(UnmanagedType.FunctionPtr)] AddFunc f, ref int b, ref double c, double[] a); public static void Main() { double c; double[] a = new double[3] { 3.0, 2.0, 4.0 }; int b = a.Length; c = 0; CallfuntoAdd(Add1, ref b, ref c, a); Console.WriteLine($" c = {c}"); } }
上面的c# 代码定义了一个CallfuntoAdd函数用来表示 fortran中的CallfuntoAdd方法。这c# 的函数名可以通过fortran 中 !DEC$ ATTRIBUTES ALIAS:'CallfuntoAdd' :: CallfuntoAdd
重新定义。在c#中使用委托来表示fortran中的回调函数。可以看到c# 中的CallfuntoAdd函数参数与 fortran中的CallfuntoAdd 函数参数的对应关系。
fortran | c# |
---|---|
integer | ref int |
real*4 | ref float |
real*8 | ref double |
一维数值 real*8, dimension(😃 :: a | double[] |
回调函数 | delegate |
c# 委托中有数组参数需要注意, 使用 IntPtr 表示数组,并且需要传入数组的大小,并且在函数中使用 Marshal.Copy
将参数复制到本地。
3. 将fortran 代码编译成动态库
这里使用 gfortran编译器。
gfortran -o test.o -c test.f90
gfortran -shared -o test.dll test.o -fPIC
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)