Ubuntu中使用C#调用Fortran编译so文件

环境说明:

  • Ubuntu版本:v22.04 LTS
  • .NET版本:v8.0.110
  • GFortran版本:v11.4.0

安装Fortran编译器

在Ubuntu上安装Fortran编译器:

# 更新包列表
sudo apt update

# 安装gfortran编译器
sudo apt install gfortran

创建.NET控制台项目

创建.NET控制台程序,首先创建一个新的.NET项目:

# 创建新的控制台项目
dotnet new console -n FortranTest
cd FortranTest

项目文件结构如下:

FortranTest/
├── fortran/
│   ├── calc.f90
├── Program.cs
├── FortranTest.csproj
└── README.md
// FortranTest.csproj
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>
  <ItemGroup>
    <None Include="libcalc.so">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
  </ItemGroup>
</Project>

// Program.cs
using System.Runtime.InteropServices;

class Program
{
    // 导入Fortran函数
    [DllImport("libcalc.so", CallingConvention = CallingConvention.Cdecl)]
    private static extern void array_sum(
        [In] double[] arr,
        int size,
        out double result);

    [DllImport("libcalc.so", CallingConvention = CallingConvention.Cdecl)]
    private static extern void array_average(
        [In] double[] arr,
        int size,
        out double result);

    static void Main(string[] args)
    {
        try
        {
            // 测试数据
            double[] testArray = { 1.0, 2.0, 3.0, 4.0, 5.0 };

            // 计算总和
            double sum = 0.0;
            array_sum(testArray, testArray.Length, out sum);
            Console.WriteLine($"数组总和: {sum}");

            // 计算平均值
            double average = 0.0;
            array_average(testArray, testArray.Length, out average);
            Console.WriteLine($"数组平均值: {average}");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"错误: {ex.Message}");
        }
    }
}
// calc.f90
module calculation
    use iso_c_binding
    implicit none
  
contains
    ! 一个简单的数组求和函数
    subroutine array_sum(arr, size, result) bind(c, name='array_sum')
        use iso_c_binding
        implicit none
  
        integer(c_int), value :: size
        real(c_double), intent(in) :: arr(size)
        real(c_double), intent(out) :: result
  
        integer :: i
        result = 0.0d0
  
        do i = 1, size
            result = result + arr(i)
        end do
    end subroutine array_sum
  
    ! 一个简单的数组平均值函数
    subroutine array_average(arr, size, result) bind(c, name='array_average')
        use iso_c_binding
        implicit none
  
        integer(c_int), value :: size
        real(c_double), intent(in) :: arr(size)
        real(c_double), intent(out) :: result
  
        call array_sum(arr, size, result)
        result = result / size
    end subroutine array_average
end module calculation
# 编译Fortran代码
cd fortran
gfortran -c -fPIC calc.f90 -o calc.o
gfortran -shared calc.o -o libcalc.so

# 复制.so文件到.NET项目目录
cp libcalc.so ../

# 编译和运行.NET程序
cd ..
dotnet build
dotnet run

能否不复制so文件到项目目录?

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>
  <ItemGroup>
    <None Include="fortran/libcalc.so">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
  </ItemGroup>
</Project>

此时,build项目后,会在输出目录新增文件“FortranTest/bin/Debug/net8.0/fortran/libcalc.so”,修改代码如下,同样可以正常运行项目

// 导入Fortran函数
[DllImport("fortran/libcalc.so", CallingConvention = CallingConvention.Cdecl)]
private static extern void array_sum(
    [In] double[] arr,
    int size,
    out double result);

[DllImport("fortran/libcalc.so", CallingConvention = CallingConvention.Cdecl)]
private static extern void array_average(
    [In] double[] arr,
    int size,
    out double result);

不建议直接在DllImport中使用相对路径。这可能会导致运行时路径解析问题。为了更可靠的路径解析,可以修改代码如下:

using System.Runtime.InteropServices;

class Program
{
    // 导入Fortran函数
    [DllImport("libcalc.so", CallingConvention = CallingConvention.Cdecl)]
    private static extern void array_sum(
        [In] double[] arr,
        int size,
        out double result);

    [DllImport("libcalc.so", CallingConvention = CallingConvention.Cdecl)]
    private static extern void array_average(
        [In] double[] arr,
        int size,
        out double result);

    static void Main(string[] args)
    {
        try
        {
            // 添加库文件搜索路径解析器
            NativeLibrary.SetDllImportResolver(typeof(Program).Assembly, (libraryName, assembly, searchPath) =>
            {
                // 获取程序运行目录下的 fortran 子目录
                string libraryPath = Path.Combine(AppContext.BaseDirectory, "fortran", libraryName);
                return NativeLibrary.Load(libraryPath);
            });

            // 测试数据
            double[] testArray = { 1.0, 2.0, 3.0, 4.0, 5.0 };

            // 计算总和
            double sum = 0.0;
            array_sum(testArray, testArray.Length, out sum);
            Console.WriteLine($"数组总和: {sum}");

            // 计算平均值
            double average = 0.0;
            array_average(testArray, testArray.Length, out average);
            Console.WriteLine($"数组平均值: {average}");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"错误: {ex.Message}");
        }
    }
}
posted @   VinciYan  阅读(15)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
点击右上角即可分享
微信分享提示