我想使用在控制台输入的命令从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子例程中吗?
答案 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#