Calling a Fortran subroutine from C# at the console

1

I want to call a Fortran subroutine from C# using commands entered at the console. 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"

 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" :

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

Two new files appear, named "fdll.dll" and "fdll.mod".

Using the Monodevelop C# text editor, I create the following C# source file called "DLLImport.cs"

 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". I type "go".

The result is a popup window telling me "go.exe has stopped working". It gives me the option to close the program. At the MS-DOS console, the following message has appeared:

 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. I am using the latest verion of gfortran (i686-w64-mingw32).

UPDATE: I modified the Fortran source code to allow for ISO_C_BINDING (following Pierre's suggestion). 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/). The new C# code is:

 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.

RESULT: when I run the program (enter "go") the same error message appears.

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
interop
asked on Stack Overflow Feb 14, 2019 by Pat Ward • edited Oct 6, 2020 by Vladimir F

3 Answers

1

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 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.

//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. The C test code gives:

  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

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()
answered on Stack Overflow Feb 15, 2019 by KL-Yang • edited Feb 18, 2019 by KL-Yang
1

The C# definition is incorrect. 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?

You can use nm, if you have it, or the dependency walker to find out what the exported symbols are.

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#.

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.

answered on Stack Overflow Feb 19, 2019 by cup • edited Feb 19, 2019 by cup
0

i686-w64-mingw32 means that you need to compile C# with x86 (not AnyCPU)

answered on Stack Overflow Oct 5, 2020 by Carlos Garcia

User contributions licensed under CC BY-SA 3.0