I have a fortran subroutine that I call from C# and want to be able to terminate. This I can do using an integer variable that I alter in C# as the fortran routine runs. However changing this to a bool / logical(c_bool) does not work. (In the code below is also an option where I change a value in a callback function.)

Working code (with int parameters) C#:

using System.Runtime.InteropServices;

Console.WriteLine("C# starting");
var cb = new Natives.ActionRefInt(OnUpdateProgress);
var terminate = 0;
var t = Task.Run(() => Natives.TestCallBackAndVariable(cb, ref terminate));
for (int i = 0; i < 4; i++)
{
    await Task.Delay(900);
    terminate = i * 100;
    Console.WriteLine($"C# loop i = {i}, terminate = {terminate}");
}
terminate = -1;
await t;


static void OnUpdateProgress(ref int val2)
{
    Console.WriteLine($"C# OnUpdateProgress received val2 = {val2}");
    val2 += 10;
    Console.WriteLine($"C# OnUpdateProgress setting val2 = {val2}");
}
public static class Natives
{

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

    [DllImport(dllName: "FortranLib.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern void TestCallBackAndVariable(
    [MarshalAs(UnmanagedType.FunctionPtr)] ActionRefInt callBack,
    ref int terminate);
}

Fortran:

subroutine TestCallBackAndVariable(callBack, terminate)
!DEC$ ATTRIBUTES DLLEXPORT::TestCallBackAndVariable
!DEC$ ATTRIBUTES DECORATE,ALIAS:"TestCallBackAndVariable" :: TestCallBackAndVariable
use ISO_C_BINDING
implicit none
! Variables
external                        ::  callBack
integer(C_INT),intent(inout)   ::  terminate
integer(C_INT)                  ::  ii
integer(C_INT)                  ::  val2
! Body of TestCallBackAndVariable
write(*,*) "Starting TestCallBackAndVariable in fortran lib"
do ii = 1,10
    call sleep(1)
    val2 = ii
    call callBack(val2)
    write(*,*) "Fortran loop ii = ",ii,", and terminate = ", terminate, ", and val from callback = ", val2
    if (terminate<0) exit
end do
write(*,*) "Finished TestCallBackAndVariable in fortran lib"
end subroutine TestCallBackAndVariable

Not working as expected code (with bool / logical) C#:

using System.Runtime.InteropServices;

Console.WriteLine("C# starting");
var cb = new Natives.ActionRefBool(OnUpdateProgress);
var terminate = false;
var t = Task.Run(() => Natives.TestCallBackAndVariable(cb, ref terminate));
for (int i = 0; i < 4; i++)
{
    await Task.Delay(900);
   
    Console.WriteLine($"C# loop i = {i}, terminate = {terminate}");
}
terminate = true;
await t;


static void OnUpdateProgress(ref bool val2)
{
    Console.WriteLine($"C# OnUpdateProgress received val2 = {val2}");
    val2 = true;
    Console.WriteLine($"C# OnUpdateProgress setting val2 = {val2}");
}

public static class Natives
{

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate void ActionRefBool(ref bool progress);

    [DllImport(dllName: "FortranLib.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern void TestCallBackAndVariable(
    [MarshalAs(UnmanagedType.FunctionPtr)] ActionRefBool callBack,
    ref bool terminate);
}

Fortran:

subroutine TestCallBackAndVariable(callBack, terminate)
!DEC$ ATTRIBUTES DLLEXPORT::TestCallBackAndVariable
!DEC$ ATTRIBUTES DECORATE,ALIAS:"TestCallBackAndVariable" :: TestCallBackAndVariable
use ISO_C_BINDING
implicit none
! Variables
external                        ::  callBack
logical(C_BOOL),intent(inout)   ::  terminate
integer(C_INT)                  ::  ii
logical(C_BOOL)                 ::  val2
! Body of TestCallBackAndVariable
write(*,*) "Starting TestCallBackAndVariable in fortran lib"
do ii = 1,10
    call sleep(1)
    val2 = .false.
    call callBack(val2)
    write(*,*) "Fortran loop ii = ",ii,", and terminate = ", terminate, ", and val from callback = ", val2
    if (terminate) exit
end do

write(*,*) "Finished TestCallBackAndVariable in fortran lib"
end subroutine TestCallBackAndVariable
subroutine TestArray(i,j,arr)

In the bool/logical variant does not c# modify value of the logical terminate fortran variable. However, changing the logical value in the OnUpdateProgress does work. (propagates back to fortran)

Help on this is, as always, much appreciated.

(VS 2022 NET 6, IFORT 2021.6.0 64bit)

推荐答案

In Fortran, the logical(kind=c_bool) type maps to the C _Bool type of the companion C processor (=compiler).

In C, _Bool is defined to be an unsigned integer type (size is implementation dependent but typically one byte) however with only two allowable values, 0 for false and 1 for true (0 and 1 here meaning the unsigned integer bit patterns that represent the integer values 0 and 1). Any other bit pattern is a trap representation and invokes undefined behavior.

However, in C# the internal representation of Bool variables is, per the ECMA CLI spec ( What is the binary representation of a boolean value in c# ), 0 for false, and all other bit patterns (that is, with any of the bits set to 1) are true.

Thus, AFAICT, the Fortran logical(c_bool) and the C# bool type are NOT interoperable, and you need some other mechanism to transfer boolean values between Fortran and C#. E.g. transferring them as integers.

Csharp相关问答推荐

使用 new() 或 default 初始化变量的区别?

有没有办法防止注入在 ASP.NET 中多次注册的接口的单个​​实现?

使用 @@ROWCOUNT 的旧 SQL 函数导致问题

在实现类 C# 中为接口成员提供显式实现时允许访问修饰符 nbot

有没有办法在避免浮点数学的同时有效地将 16 位 colored颜色 转换为 24 位 colored颜色 ?

C# 扩展一个 listView 包含具有自定义对象的列

WebDriverManager - '远程服务器返回错误:(404)未找到.'

C# 为什么属性的 get 和 set 方法都称为 accessor 而不是 accessor 和 mutator?

更新中间件 .NET Core 中的响应主体

在计时器函数中更新 TextBlock 文本时出现 WinUI 3 COMException 错误

Linq:将 GroupBy 结果传递给函数

使用测试服务器的 .NET 6 E2E 测试在 TeamCity CI 上失败,但工作手动运行

.NET Core 6,选项模式,如何从 appsettings 获取 json 对象数组

带有 React 的 .NET 6 不会连接到 SignalR

任务被取消,但不清楚原因和方式

移动到圆圈区域,然后捕捉到网格?

在不同项目的不同 c# 类中使用 appsettings 值

创建重复的项目并将它们提交到列表中

是否有任何(可逆)方式(在c#中)将字符串转换为较小的字符串,当我说较小时,我的意思是“长度减少”?

Sveltekit + NET 6 C# Api - 登录后,我无法调用具有授权的 api