从 C# 中的调用函数更改 Fortran 中的引用参数值

Changing reference parameter value in fortran from calling function in c#

我有一个用 fortran 编写的 dll,其中包含一个将逻辑参数作为输入的子例程。我想用它来控制子例程的取消,并有可能在调用 c# 代码中更改它的值。下面是我测试过的一个小例子。

C#代码:

using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;

namespace TestConsApp
{
    internal static class Program
    {
        private static bool _b;
        [DllImport(dllName: "TestFortran.dll", CallingConvention = CallingConvention.Cdecl)]
        internal static extern void TestBool(ref bool b);


        private static void Main(string[] args)
        {    
            TestFortranBool();
            Console.ReadLine();
        }


        private static void TestFortranBool()
        {
            _b = true;
            var t = Task.Factory.StartNew(() => TestBool(ref _b));
            Console.WriteLine($"C# :{_b}");
            Thread.Sleep(5000);
            _b = false;
            Console.WriteLine($"C# :{_b}");
            Thread.Sleep(5000);
            _b = true;
            Console.WriteLine($"C# :{_b}");
            Thread.Sleep(5000);
            _b = false;
            Console.WriteLine($"C# :{_b}");
            Thread.Sleep(5000);
            _b = true;
            Console.WriteLine($"C# :{_b}");

            t.Wait();
        }
    }
}

和 Fortran 代码:

module FortranTesting
  use iso_c_binding
  implicit none
contains
  
  
  subroutine TestBool(b)

  ! Expose subroutine TestFortran to users of this DLL
  !
  !DEC$ ATTRIBUTES DLLEXPORT::TestBool
  !DEC$ ATTRIBUTES DECORATE,ALIAS:"TestBool" :: TestBool
  
  logical(c_bool),intent(in) ::  b
  write(*,*) "Inside fortran"
  write(*,*) "F :", b
  call sleep(5)
  write(*,*) "F : ", b
  call sleep(5)
  write(*,*) "F : ", b
  call sleep(5)
  write(*,*) "F : ", b
  
  end subroutine TestBool
end module FortranTesting

我从中得到的是:

C# :True
Inside fortran
F : T

C# :False
F : T

C# :True
F : T

C# :False
F : T

C# :True

我想要的是 F (fortran) 与 C# 交替使用。 当从 C# 读取变量并在 Fortran 中更改变量时,我尝试了相反的方法,这按预期工作。

我不会C#,所以在这方面帮不了你。但是,只要您可以将 C# 与 C 接口,也可以通过一种方法将 Fortran 代码连接到 C#。为此,您必须将 bind(C) 属性添加到您的 Fortran 代码中,

module FortranTesting
    use iso_c_binding
    implicit none
contains
    subroutine TestBool(b) bind(C, name = "TestBool")
        ! Expose subroutine TestFortran to users of this DLL
        !DEC$ ATTRIBUTES DLLEXPORT :: TestBool

        use, intrinsic :: iso_c_binding, only: c_bool
        implicit none
        logical(c_bool), intent(in) ::  b

        write(*,*) "Inside Fortran:"
        write(*,*) "F :", b
        call sleep(5)
        write(*,*) "F : ", b
        call sleep(5)
        write(*,*) "F : ", b
        call sleep(5)
        write(*,*) "F : ", b

    end subroutine TestBool
end module FortranTesting

此代码现在应该可以从 C 调用为 TestBool(b),输入参数 b 的类型为 _Bool.

我终于找到了解决取消的方法。 我给出了一个示例代码,该代码 returns 是 fortran 代码的一个值(因此可用于取消 fortran subroutine/function):

c#代码:

using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace TestConsApp
{
    internal static class Program
    {
        [DllImport(dllName: "TestFortran.dll", CallingConvention = CallingConvention.Cdecl)]
        internal unsafe static extern void TestCallBack([MarshalAs(UnmanagedType.FunctionPtr)] ActionRefInt callBack);

        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate void ActionRefInt(ref int i);


        private static void Main(string[] args)
        {
            TestCallBack();
            Console.ReadLine();
        }

        private static void TestCallBack()
        {
            var callbackHandler = new ActionRefInt(OnCallBackInt);
            TestCallBack(callbackHandler);
        }

        private static void OnCallBackInt(ref int i)
        {
            Console.WriteLine($"C#: Value before change: {i}");
            i++;
            Console.WriteLine($"C#: Value after change: {i}");
        }

    }
}

和 Fortran 代码:

  module FortranTesting
  use iso_c_binding
  implicit none

  contains

  subroutine TestCallBack(callBack)
  !DEC$ ATTRIBUTES DLLEXPORT::TestCallBack
  !DEC$ ATTRIBUTES DECORATE,ALIAS:"TestCallBack" :: TestCallBack
  external        ::  callBack
  integer         ::  test
  
  test  = 1
  write(*,*) "F: Test before callback: ", test
  call callBack(test)
  write(*,*) "F: Test after callback: ", test
  end subroutine TestCallBack
  end module FortranTesting

此代码产生以下输出:

F: Test before callback: 1

C#: Value before change: 1

C#: Value after change: 2

F: Test after callback: 2