我有一个Fortran子 routine ,我从C#调用它,并希望能够终止它.这可以使用我在Fortran routine 运行时在C#中更改的整型变量来完成.但是,将其更改为bool/logical(c_bool)不起作用.(在下面的代码中也有一个选项,我可以在其中更改回调函数中的值.)

工作代码(具有int个参数) 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

未按预期代码工作(使用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)

在bool/逻辑变量中,c#不修改逻辑terminate Fortran变量的值.但是,更改OnUpdateProgress中的逻辑值确实有效.(传播回Fortran)

在这方面的帮助,一如既往地受到高度赞赏.

(VS 2022 Net 6、IFORT 2021.6.0 64位)

推荐答案

在Fortran中,logical(kind=c_bool)类型映射到配套的C处理器的C_Bool类型(=编译器).

在C中,_Bool被定义为无符号整数类型(大小取决于实现,但通常是一个字节),但只有两个允许值,0代表FALSE,1代表TRUE(这里的0和1表示代表整数值0和1的无符号整数位模式).任何其他位模式都是trap 表示,并调用未定义的行为.

然而,在C#中,根据ECMA CLI规范(What is the binary representation of a boolean value in c#),布尔变量的内部表示为0,表示假,而所有其他位模式(即,任何位设置为1)都为真.

因此,AFAICT、Fortran logical(c_bool)和C#bool类型不能互操作,您需要一些其他机制来在Fortran和C#之间传输布尔值.例如,将它们作为整数进行传输.

Csharp相关问答推荐

通过EFCore上传大量数据.

迭代C#List并在数据库中 for each 元素执行SELECT语句—更有效的方式?

从应用程序图API调用访问所有者字段

将轮询与超时同步

将现有字段映射到EFCore中的复杂类型

为什么我的表单在绑定到对象时提交空值?

C#使用TextFieldParser读取.csv,但无法使用";0";替换创建的列表空条目

Selify只更改第一个下拉菜单,然后忽略REST-C#

TagHelpers在新区域不起作用

异步任务调用程序集

MudBlazor Textfield已禁用,但其验证工作正常

如何让游戏对象在切换场景时被销毁,但在开始新游戏时重新锁定

我的命名管道在第一次连接后工作正常,但后来我得到了System.ObjectDisposedException:无法访问关闭的管道

类/值和日期的泛型方法

如何将默认区域性更改为fr-FR而不是en-US?

在平行内使用跨度.用于循环

如何从SignalR获取连接客户端的域

在ObservableCollection上使用[NotifyPropertyChangedFor()]源代码生成器不会更新UI

分别切换用于读取和写入的EF核心日志(log)

如何提高C#中比较大 struct 的性能?