[英]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.