1
votes

Appel d'un sous-programme Fortran à partir de C # sur la console

Je veux appeler un sous-programme Fortran à partir de C # en utilisant des commandes entrées dans la console. J'essaie depuis deux jours maintenant, en lisant de nombreuses pages Web et en suivant de nombreux conseils, mais sans succès.

Voici un exemple typique de mes nombreuses tentatives infructueuses.

Utilisation d'un éditeur de texte ( Bloc-notes) Je crée ce fichier appelé "fdll.f90"

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

Sur la console MS-DOS (CMD.EXE), je tape la commande suivante et appuie sur "Entrée":

 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

Deux nouveaux fichiers apparaissent, nommés "fdll.dll" et "fdll.mod".

En utilisant l'éditeur de texte Monodevelop C #, je crée le suivant le fichier source C # appelé "DLLImport.cs"

 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)

Sur la console, j'entre la commande suivante:

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

Un nouveau fichier apparaît appelé "go.exe". Je tape "go".

Le résultat est une fenêtre contextuelle m'indiquant que "go.exe a cessé de fonctionner". Cela me donne la possibilité de fermer le programme. Sur la console MS-DOS, le message suivant est apparu:

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

Qu'ai-je fait de mal? Comment puis-je faire fonctionner cela?

J'utilise un ordinateur portable SONY 64 bits sous Windows 8.1. J'utilise la dernière version de gfortran (i686-w64-mingw32).

MISE À JOUR: J'ai modifié le code source de Fortran pour permettre ISO_C_BINDING (suite à la suggestion de Pierre). La nouvelle version est:

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

J'ai également modifié le code source C # pour lui faire envoyer la chaîne de caractères dans Fortran sous forme de tableau (comme expliqué ici: http://www.luckingtechnotes.com/calling-fortran-dll-from-csharp/ a >). Le nouveau code C # est:

 module fdll
implicit none
 contains

 subroutine testFDLL(char)
 character(12) :: char
    write(6,*)" Hello FORTRAN : let us do something ...",char
 return
 end
 end module

Je n'ai apporté aucune modification aux arguments de ligne de commande exécutant les compilateurs; ni compile, ni gfortan ni csc, ne se sont plaints de l'un de ces changements.

RÉSULTAT: lorsque j'exécute le programme (entrez "go"), le même message d'erreur apparaît.

Quelqu'un peut expliquez ce qui ne va pas ou ce qui manque avec ce que j'ai fait. Est-ce vraiment difficile de faire en sorte que C # envoie une chaîne de caractères dans un sous-programme Fortran?


6 commentaires

Avez-vous lu les fonctionnalités iso_c_binding de Fortran?


Merci. Si vous pouvez me montrer où cela entre dans mon code source ou dans mes commandes, je l'apprécierais.


iso_c_binding est un ensemble de fonctionnalités, y compris des spécificateurs qui organisent une convention d'appel de style C (ABI) entre Fortran et C (pas C #, vous devez considérer le code comme C). C'est un sujet bien connu, il est donc de votre devoir au moins de l'essayer et de publier votre code adapté ici avant que quiconque ne fournisse une aide supplémentaire.


Sûr. Je vais essayer. Dans toutes mes recherches sur ce sujet (interopérabilité de C # et FORTRAN) il n'y a pas eu de mention de "iso_c_binding". Mais je vais l'examiner.


Fortran n'a pas de capacité directe "C #". Vous devez le considérer comme une interface C.


Je ne sais pas si cela aidera en essayant d'ajouter un -fPIC à la ligne de compilation gfortran


3 Réponses :


1
votes

J'essaie juste de montrer comment interfacer ce code FORTRAN avec C, cela ne répond pas complètement à votre question, mais si vous savez comment interfacer C (prétendre le FORTRAN comme C) avec C #, cela devrait aider.

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

Et le code C appelant le sous-programme FORTRAN.

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

Vous pouvez faire semblant que votre sous-programme FORTRAN est une fonction C et appeler depuis C # comme d'habitude. Le code de test C donne:

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

Vous pouvez également établir un lien implicite avec la bibliothèque dynamique comme suit (notez, ignorez tout contrôle d'erreur et fermez les ressources pour un exemple plus court).

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

La commande pour les compiler (sous Linux) est

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

La sortie du 2ème programme est comme: p>

!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


4 commentaires

Merci. J'ai changé mon code FORTRAN pour qu'il soit identique au vôtre, mais le message d'erreur demeure. Votre code C est intéressant. Je ne vois pas comment modifier mon code C # pour le refléter. Des idées?


Vous devez identifier le problème FORTRAN, le problème C #, le problème d'interface ou le problème d'environnement d'exécution. Je suppose que vous savez comment appeler la bibliothèque de dll C à partir de C #, car je ne connais pas C #. Vous pouvez essayer d'utiliser C comme intermédiaire, appelez d'abord C dll à partir de C # pour éliminer les problèmes possibles qui ne sont pas liés à FORTRAN. Mon exemple est juste la partie liée à FORTRAN. Essayez d'abord d'isoler les problèmes.


Comment avez-vous compilé votre code FORTRAN? Pouvez-vous me donner la syntaxe exacte de la ligne de commande de votre console.


Je viens de le compiler comme gfortran -c x.f90 pour tester le code. Cela peut ne pas être utile car vous souhaitez le compiler dans une bibliothèque de liens dynamiques. Comme je l'ai dit, l'exemple montre simplement l'interopérabilité FORTRAIN et C, votre problème exact peut être sur une autre partie de la chaîne.



1
votes

La définition C # est incorrecte. Cela devrait être

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

voir comment appeler une fonction Fortran90 incluse dans un module en code c ++?

Vous pouvez utiliser nm, si vous l'avez, ou le le navigateur de dépendances pour savoir quels sont les symboles exportés.

Notez que C # char est de 2 octets, Fortran char est de 1 octet et la façon dont les tableaux sont stockés est différente à la fois en Fortran et C #.

S'il ne s'agit que d'un test d'interopérabilité, essayez d'abord de travailler uniquement avec des entiers et assurez-vous que cela fonctionne. Passez ensuite à un seul caractère (octet), puis aux tableaux. Ne passez pas aux tableaux lors de votre première tentative.


0 commentaires

0
votes

i686-w64-mingw32 signifie que vous devez compiler C # avec x86 (pas AnyCPU)


0 commentaires