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# 中声明方法的时候需要注意以下几点:

  1. 函数应该声明为 static, extern 方法
  2. 函数的参数类型需要注意,这里仅仅以fortran为例。下文介绍了几种常见参数传递的对应情况。

DllImport方法有几个参数也需要注意:

  1. EntryPoint:这需要使用一些第三方工具从dll / so 文件中查找。win下使用vs 带的 dumpbin.exe方法,使用dumpbin /EXPORTS target.dll 命令查看。linux 下使用 nm -D target.so 查找定义的方法的entrypoint。
  2. 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

posted @   hugowu  阅读(115)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示