简体   繁体   English

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

[英]Calling a Fortran subroutine from C# at the console

I want to call a Fortran subroutine from C# using commands entered at the console.我想使用在控制台输入的命令从 C# 调用 Fortran 子例程。 I have been trying for two days now, reading many web pages, and following much advice, but with no success.我已经尝试了两天,阅读了很多网页,并遵循了很多建议,但没有成功。

Here is a typical example of my many failed attempts.这是我多次失败尝试的典型例子。

Using a text editor (Notepad) I create this file called "fdll.f90"使用文本编辑器(记事本)我创建了这个名为“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

At the MS-DOS console (CMD.EXE), I type the following command and press "Enter" :在 MS-DOS 控制台 (CMD.EXE) 上,我键入以下命令并按“Enter”:

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

Two new files appear, named "fdll.dll" and "fdll.mod".出现两个新文件,名为“fdll.dll”和“fdll.mod”。

Using the Monodevelop C# text editor, I create the following C# source file called "DLLImport.cs"使用 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);
}

At the console, I enter the following command :在控制台,我输入以下命令:

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

A new file appears called "go.exe".出现一个名为“go.exe”的新文件。 I type "go".我输入“去”。

The result is a popup window telling me "go.exe has stopped working".结果是一个弹出窗口告诉我“go.exe 已停止工作”。 It gives me the option to close the program.它让我可以选择关闭程序。 At the MS-DOS console, the following message has appeared:在 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)

What have I done wrong?我做错了什么? How can I make this work?我怎样才能使这项工作?

I am using a 64-bit SONY laptop running Windows 8.1.我使用的是运行 Windows 8.1 的 64 位 SONY 笔记本电脑。 I am using the latest verion of gfortran (i686-w64-mingw32).我正在使用最新版本的 gfortran (i686-w64-mingw32)。

UPDATE: I modified the Fortran source code to allow for ISO_C_BINDING (following Pierre's suggestion).更新:我修改了 Fortran 源代码以允许 ISO_C_BINDING(按照 Pierre 的建议)。 The new version is:新版本是:

 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

I also modified the C# source code to make it send the character string into Fortran as an array (as explained here: http://www.luckingtechnotes.com/calling-fortran-dll-from-csharp/ ).我还修改了 C# 源代码,使其将字符串作为数组发送到 Fortran(如此处所述: http : //www.luckingtechnotes.com/calling-fortran-dll-from-csharp/ )。 The new C# code is:新的 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);
 }

I made no changes to the command line arguments running the compilers ;我没有更改运行编译器的命令行参数; neither compile, gfortan nor csc, complained about any of these changes. compile、gfortan 和 csc 都没有抱怨这些更改中的任何一个。

RESULT: when I run the program (enter "go") the same error message appears.结果:当我运行程序(输入“go”)时,出现相同的错误消息。

Can somebody please explain what is wrong, or missing, with what I have done.有人可以解释一下我所做的有什么问题或遗漏了什么。 Is it really this hard getting C# to send a character string into a Fortran subroutine?让 C# 将字符串发送到 Fortran 子例程中真的那么难吗?

I just try to show how to interface this FORTRAN code with C, this does not fully answer your question, but if you know how to interface C (pretend the FORTRAN as C) with C#, it should help.我只是试图展示如何将这个 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

And the C code calling FORTRAN subroutine.以及调用 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;
}

You can pretend your FORTRAN subroutine as a C function and call from C# as usually ways.您可以将 FORTRAN 子例程假装为 C 函数,并像通常那样从 C# 调用。 The C test code gives: C测试代码给出:

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

You can also implicitly link with the dynamic library as the following (note, ignore all error check and close of resources for shorter example).您还可以按如下方式隐式链接动态库(注意,对于较短的示例,忽略所有错误检查和资源关闭)。

//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;
}

The command to compile them (on linux) are编译它们的命令(在 linux 上)是

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

The output of the 2nd program is like:第二个程序的输出是这样的:

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

The C# definition is incorrect. C# 定义不正确。 It should be它应该是

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

see how to call a Fortran90 function included in a module in c++ code?看看如何在 C++ 代码中调用模块中包含的 Fortran90 函数?

You can use nm, if you have it, or the dependency walker to find out what the exported symbols are.您可以使用 nm(如果有)或依赖项walker 来找出导出的符号是什么。

Note that C# char is 2 bytes, Fortran char is 1 byte and the way arrays are stored is different in both Fortran and C#.请注意,C# char 是 2 个字节,Fortran char 是 1 个字节,并且在 Fortran 和 C# 中存储数组的方式不同。

If this is just an interoperability test, try working with just integers first and make sure that works.如果这只是一个互操作性测试,请先尝试仅使用整数并确保其有效。 Then move on to a single character (byte) and then on to arrays.然后转到单个字符(字节),然后转到数组。 Don't go on to arrays in your first attempt.第一次尝试时不要继续使用数组。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM