Domanda

Sto provando a chiamare una dll legacy compilata dal codice FORTRAN. Sono nuovo di Interop, ma ho letto alcuni articoli su di esso e sembra che il mio caso dovrebbe essere abbastanza semplice.

Il metodo che voglio davvero chiamare ha una firma del metodo complessa, ma non posso nemmeno chiamare questo semplice metodo GetVersion senza ottenere una violazione della memoria protetta.

Ecco il mio codice DllImport:

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

Ecco il codice 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

Ecco il mio test unit che fallisce:

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

Ecco il messaggio di errore che ricevo:

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

Altre cose che ho provato:

  • Utilizzo del marshalling predefinito
  • Passaggio di un carattere [] anziché di una stringa (ottenere invece errori di firma del metodo)
È stato utile?

Soluzione 2

OK, l'ho fatto funzionare, il problema stava passando per rif. Non sono sicuro del perché, ma funziona:

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

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

Altri suggerimenti

... snip ... OK, l'ho fatto funzionare, il problema stava passando per ref. Non sono sicuro del perché, ma funziona: ... snip ...

Devi passare per riferimento perché quello è il semantico utilizzato dal codice FORTRAN. Il codice client passa in un buffer su cui scriverà il codice FORTRAN anziché utilizzare un valore restituito.

... snip ... ! MS $ ATTRIBUTES REFERENCE :: VRSION ... snip ...

Questo attributo nel tuo codice FORTRAN specifica che questo parametro viene passato per riferimento. Ciò significa che il codice FORTRAN scriverà a questo indirizzo. Se DllImport non lo dichiara anche come valore di riferimento, si otterrà una violazione di accesso.

Hai provato a usare StringBuilder?

Crea la tua String come StringBuilder e passala nella funzione dll.

Non sono sicuro di quale affermazione Marashlling utilizzare, forse il default potrebbe funzionare.

Dai un'occhiata a: Marshal C ++ & # 8220; string & # 8221; classe in C # P / Invoke

Ecco un buon articolo che potrebbe anche aiutare: Interop Marshalling

Non riesco a provare questa soluzione poiché non ho un compilatore FORTRAN, ma penso che questo funzionerebbe per te:

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

Grazie a tutti ragazzi, ho provato a passare una stringa da c # a una subroutine da fortran dll e questo metodo è stato l'unico a funzionare tra molti altri

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top