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?
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()
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.
i686-w64-mingw32 means that you need to compile C# with x86 (not AnyCPU)
User contributions licensed under CC BY-SA 3.0