【问题标题】:Calling a Fortran subroutine from C# at the console在控制台从 C# 调用 Fortran 子例程
【发布时间】:2019-07-08 07:04:45
【问题描述】:

我想使用在控制台输入的命令从 C# 调用 Fortran 子例程。 我已经尝试了两天,阅读了很多网页,并听取了很多建议,但没有成功。

这是我多次失败尝试的典型例子。

我使用文本编辑器(记事本)创建了这个名为“fdll.f90”的文件

 module fdll
implicit none
 contains

 subroutine testFDLL(char)
 character(12) :: char
    write(6,*)" Hello FORTRAN : let us do something ...",char
 return
 end
 end module

在 MS-DOS 控制台 (CMD.EXE),我输入以下命令并按“Enter”:

 C:\Compilers\fortran\mingw32\bin\gfortran.exe -shared -o fdll.dll fdll.f90 

出现两个新文件,名为“fdll.dll”和“fdll.mod”。

使用 Monodevelop C# 文本编辑器,我创建了以下名为“DLLImport.cs”的 C# 源文件

 using System;
 using System.Runtime.InteropServices;

public static class DLLImport
{       
    public static void Main(string[] args)
    {
        RunFortranDLL ();
    }
    
public static void RunFortranDLL()
    {
        FortranLib.testFDLL("Please work!");
    }
}

public static class FortranLib
{
    private const string dllName = "fdll.dll";
    [DllImport(dllName, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]

    public static extern void testFDLL(string Plea);
}

在控制台,我输入以下命令:

 C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe /t:exe /out:go.exe DLLImport.cs 

出现一个名为“go.exe”的新文件。我输入“go”。

结果是一个弹出窗口告诉我“go.exe 已停止工作”。它让我可以选择关闭程序。在 MS-DOS 控制台上,出现以下消息:

 Unhandled Exception: System.BadImageFormatException: An attempt was made to load a program with an incorrect format. (Exception from HRESULT: 0x8007000B)
 at Fortran.Lib.testFDLL(String Plea)
 at DLLImport.Main(String[] args)

我做错了什么?我怎样才能做到这一点?

我使用的是运行 Windows 8.1 的 64 位 SONY 笔记本电脑。我正在使用最新版本的 gfortran (i686-w64-mingw32)。

更新:我修改了 Fortran 源代码以允许 ISO_C_BINDING(遵循 Pierre 的建议)。新版本是:

 module fdll
 contains
 subroutine testFDLL(char) bind(C)
     USE ISO_C_BINDING
     character (C_CHAR) :: char(20)
    write(6,*)" Hello FORTRAN : let us do something ..."
 return
 end subroutine
 end module

我还修改了 C# 源代码,使其将字符串作为数组发送到 Fortran(此处解释为:http://www.luckingtechnotes.com/calling-fortran-dll-from-csharp/)。新的 C# 代码是:

 using System;
 using System.Runtime.InteropServices;

 public static class DLLImport
 {       
public static void Main(string[] args)
{
    RunFortranDLL ();
}

public static void RunFortranDLL()
{
    FortranLib.testFDLL(ToCharacterArrayFortran("Please work!",20));
}

public static char[] ToCharacterArrayFortran(this string source, int length)
{
    var chars = new char[length];
    int sourceLength = source.Length;
    for (int i = 0; i < length; i++)
    {
        if (i < sourceLength)
            chars[i] = source[i];
        else
            chars[i] = ' '; // Important that these are blank for Fortran compatibility.
    }

    return chars;
   }
 }

 public static class FortranLib
 {
     private const string dllName = "fdll.dll";
     [DllImport(dllName, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]

public static extern void testFDLL(char[] Plea);
 }

我没有更改运行编译器的命令行参数; compile、gfortan 和 csc 都没有抱怨这些变化。

结果:当我运行程序时(输入“go”),出现相同的错误消息。

有人可以解释一下我所做的事情有什么问题或遗漏。让 C# 将字符串发送到 Fortran 子程序中真的很难吗?

【问题讨论】:

  • 您是否了解过 Fortran 的 iso_c_binding 功能?
  • 谢谢。如果您能告诉我源代码或命令的位置,我将不胜感激。
  • iso_c_binding 是一组功能,包括安排 Fortran 和 C 之间的 C 风格调用约定 (ABI) 的说明符(不是 C#,您必须将代码视为 C)。这是一个众所周知的话题,因此您的任务是至少在任何人提供进一步帮助之前尝试并在此处发布您修改后的代码。
  • 当然。我会试试看。在我对这个主题(C# 和 FORTRAN 的互操作性)的所有研究中,都没有提到“iso_c_binding”。但我会调查的。
  • Fortran 没有直接的“C#”功能。您需要将其视为 C 接口。

标签: c# fortran interop


【解决方案1】:

我只是试图展示如何将此 FORTRAN 代码与 C 接口,这并不能完全回答您的问题,但如果您知道如何将 C(将 FORTRAN 伪装成 C)与 C# 接口,它应该会有所帮助。

!fortran code, named as x.f90
module fdll
    implicit none
contains

subroutine testFDLL(str, n) bind(c, name='testFDLL_as_C')
    use ISO_C_BINDING
    integer(c_int), value :: n
    character(kind=c_char), intent(in) :: str(n)
    write(6,*)" Hello FORTRAN : let us do something ...",str
    return
end
end module

以及调用FORTRAN子程序的C代码。

//c code explicitly link. named as y.c
#include <stdio.h>
#include <string.h>

int main()
{
    void testFDLL_as_C(char *str, int n);
    char str[] = "Hello from C";
    testFDLL_as_C(str, strlen(str));
    return 0;
}

您可以将您的 FORTRAN 子例程伪装成 C 函数,并像通常的方式一样从 C# 调用。 C测试代码给出:

  Hello FORTRAN : let us do something ...Hello from C

您还可以隐式链接到动态库,如下所示(注意,对于较短的示例,忽略所有错误检查和资源关闭)。

//implicit link. named as z.c
#include <stdio.h>
#include <string.h>
#include <dlfcn.h>

int main()
{
    void (*func_from_so_f90)(char *str, int n);
    char str[] = "Hello from C, again, using dynamic dlopen()";
    void *handle = dlopen("./libxf90.so", RTLD_LAZY);
    func_from_so_f90 = dlsym(handle, "testFDLL_as_C");
    func_from_so_f90(str, strlen(str));
    return 0;
}

编译它们的命令(在 linux 上)是

gfortran -o libxf90.so -shared -fPIC x.f90
gcc -o yout y.c ./libxf90.so
gcc -o zout z.c -ldl

第二个程序的输出是这样的:

  Hello FORTRAN : let us do something ...Hello from C, again, using dynamic dlopen()

【讨论】:

  • 谢谢。我将我的 FORTRAN 代码更改为与您的相同,但错误消息仍然存在。你的 C 代码很有趣。我看不到如何修改我的 C# 代码来镜像它。有什么想法吗?
  • 您需要确定是FORTRAN问题、C#问题、接口问题还是运行时环境问题。我假设你知道如何从 C# 调用 C dll 库,因为我不懂 C#。您可以尝试使用 C 作为中间体,首先从 C# 调用 C dll 以消除可能与 FORTRAN 无关的问题。我的例子只是与 FORTRAN 相关的部分。首先尝试隔离问题。
  • 你是如何编译你的 FORTRAN 代码的?你能给我你的控制台命令行的确切语法吗?
  • 我只是将它编译为 gfortran -c x.f90 来测试代码。它可能没有帮助,因为您想将其编译为动态链接库。正如我所说,该示例仅显示 FORTRAIN 和 C 互操作性,您的确切问题可能在链的其他部分。
【解决方案2】:

C# 定义不正确。应该是

public static extern void __MOD_fdll_testFDLL(byte[] Plea);

how to call a Fortran90 function included in a module in c++ code?

如果你有 nm,你可以使用 nm,或者依赖 walker 来找出导出的符号是什么。

请注意,C# char 为 2 个字节,Fortran char 为 1 个字节,Fortran 和 C# 中数组的存储方式不同。

如果这只是一个互操作性测试,请先尝试仅使用整数并确保其有效。然后转到单个字符(字节),然后转到数组。不要在第一次尝试时继续使用数组。

【讨论】:

    【解决方案3】:

    i686-w64-mingw32 表示需要用x86(不是AnyCPU)编译C#

    【讨论】:

      猜你喜欢
      • 2014-01-14
      • 2012-01-02
      • 1970-01-01
      • 1970-01-01
      • 2016-10-13
      • 2016-03-09
      • 1970-01-01
      • 2012-05-03
      • 1970-01-01
      相关资源
      最近更新 更多