在控制台上从C#调用FORTRAN子例程

时间:2019-02-14 09:39:34

标签: c# fortran interop

我想使用在控制台输入的命令从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);
 }

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

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

有人可以对我所做的事情进行解释吗?真的很难让C#将字符串发送到FORTRAN子例程中吗?

3 个答案:

答案 0 :(得分: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

还有C代码调用FORTRAN子例程。

//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()

答案 1 :(得分:1)

C#定义不正确。应该是

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

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

您可以使用nm(如果有)或依赖项遍历器来找出导出的符号是什么。

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

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

答案 2 :(得分:0)

i686-w64-mingw32意味着您需要使用x86(不是AnyCPU)编译C#