2008-10-23 15 views
4

J'essaye d'appeler à un dll hérité compilé à partir du code FORTRAN. Je suis nouveau à Interop, mais j'ai lu quelques articles à ce sujet et il semble que mon cas devrait être assez simple.Violation de mémoire protégée appelant DLL FORTRAN à partir de C#

La méthode que je veux vraiment appeler a une signature de méthode complexe, mais je ne peux même pas appeler cette méthode GetVersion simple sans obtenir une violation de la mémoire protégée.

Voici mon code DllImport:

[DllImport("GeoConvert.dll", 
      EntryPoint="[email protected]", 
      CallingConvention=CallingConvention.StdCall)] 
public static extern void GetGeoConvertVersion([MarshalAs(UnmanagedType.LPStr, SizeConst=8)] 
                ref string version); 

Voici le code FORTRAN:

SUBROUTINE GetVer(VRSION) 
C 
!MS$DEFINE MSDLL 
!MS$IF DEFINED (MSDLL) 
     ENTRY Get_Version (VRSION) 
     !MS$ATTRIBUTES DLLEXPORT,STDCALL :: Get_Version 
     !MS$ATTRIBUTES REFERENCE :: VRSION 
!MS$ENDIF 
!MS$UNDEFINE MSDLL 
C 
    CHARACTER*8 VRSION 
C 
    VRSION = '1.0a_FhC'           
C 
    RETURN 
    END 

Voici mon test unitaire qui échoue:

[Test] 
public void TestGetVersion() 
{ 
    string version = ""; 
    LatLonUtils.GetGeoConvertVersion(ref version); 
    StringAssert.IsNonEmpty(version); 
} 

Voici le message d'erreur que je reçois:

System.AccessViolationException 
Message: Attempted to read or write protected memory. 
     This is often an indication that other memory is corrupt. 

Autres choses que j'ai essayé:

  • Utilisation de la valeur par défaut marshalling
  • Le passage d'un char [] au lieu d'une chaîne (obtenir des erreurs de signature de la méthode à la place)
+0

Je suis un peu choqué que vous » utilisez toujours un compilateur Microsoft Fortran. (Leur dernier compilateur Win32 Fortran était en 1994, n'est-ce pas?) La plupart des gens que je connais qui développent Fortran sous Windows utilisent soit DEC/Compaq/HP/Intel Visual Fortran ou Gfortran dérivé de MinGW. –

+0

D'un autre côté, si le code binaire fonctionne, il n'y a pas de raison de recompiler. C'est probablement un fort désincitatif à améliorer. –

Répondre

1

OK, je l'ai à travailler, le problème passait ref. Je ne sais pas pourquoi, mais cela fonctionne:

[DllImport("GeoConvert.dll", 
       EntryPoint="[email protected]", 
       CallingConvention=CallingConvention.StdCall)] 
    public static extern void GetGeoConvertVersion([MarshalAs(UnmanagedType.LPArray)] 
                byte[] version); 

Avec ce test:

[Test] 
    public void TestGetVersion() 
    { 
     //string version = ""; 
     byte[] version = new byte[8]; 
     LatLonUtils.GetGeoConvertVersion(version); 
     char[] versionChars = System.Text.Encoding.ASCII.GetChars(version); 

     string versionString = new string(versionChars); 
    } 
+1

J'ai juste essayé ceci et cela a fonctionné magnifiquement :) Un commentaire tho - vous pouvez employer 'System.Text.Encoding.ASCII.GetString()' si vous voulez remplacer vos deux dernières lignes de code par une seule ligne. Un peu plus propre, mais fonctionne dans les deux sens. Merci d'avoir posté la mise à jour! Cela m'a vraiment aidé. – Mike

0

Avez-vous essayé d'utiliser un StringBuilder?

Créez votre String en tant que StringBuilder et transmettez-le dans la fonction dll. Je ne suis pas sûr de ce que Marashlling instruction à utiliser, perhapse le défaut pourrait fonctionner.

Jetez un oeil à: Marshal C++ “string” class in C# P/Invoke

Heres un bon article pourrait l'aider ainsi: Interop Marshalling

0

Je ne peux pas essayer cette solution car je n'ai pas un compilateur FORTRAN, mais je pense que cela fonctionnerait pour vous:

[DllImport("GeoConvert.dll", 
      EntryPoint="[email protected]", 
      CallingConvention=CallingConvention.StdCall, 
      CharSet=CharSet.Ansi)] 
    public static extern void GetGeoConvertVersion(StringBuilder version); 
3

... snip ... OK, je l'ai eu à travailler, le problème passait ref. Je ne sais pas pourquoi, mais cela fonctionne: ... snip ...

Vous devez passer par référence car c'est la sémantique utilisée par le code FORTRAN. Le code client passe dans un tampon auquel le code FORTRAN va écrire au lieu d'utiliser une valeur de retour.

... snip ... ! MS $ attributes RÉFÉRENCE :: VRSION ... snip ...

Cet attribut dans votre code FORTRAN précise que ce paramètre est passé par référence.Cela signifie que le code FORTRAN va écrire à cette adresse. Si le DllImport ne le déclare pas comme valeur de référence, vous obtiendrez une violation d'accès.

+0

Pourquoi le pragma appel-par-référence doit-il être là pour commencer? Est-ce que Microsoft Fortran n'utilise pas l'appel par référence par défaut, comme les autres compilateurs Fortran? (Ou est appelé par valeur la valeur par défaut seulement pour les DLL?) –

+0

tflanagan, pas tout à fait. Passer par référence a fait échouer, la valeur par défaut a réussi, même si FORTRAN demande clairement une référence. Le problème est qu'un octet [] est déjà un pointeur, donc passer un octet [] par ref est un pointeur sur un pointeur, donc j'ai une erreur d'accès à la mémoire. – brien

0

Merci à tous les gars, j'ai essayé de passer une chaîne de C# à un sous-programme de dll Fortran et cette méthode était la seule travaille parmi beaucoup d'autres