Pregunta

Estoy intentando llamar a una dll heredada compilada desde el código FORTRAN. Soy nuevo en Interop, pero he leído algunos artículos al respecto y parece que mi caso debería ser bastante sencillo.

El método al que realmente quiero llamar tiene una firma de método complejo, pero ni siquiera puedo llamar a este simple método GetVersion sin obtener una violación de memoria protegida.

Aquí está mi código DllImport:

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

Aquí está el código 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

Aquí está mi prueba de unidad que falla:

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

Aquí está el mensaje de error que recibo:

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

Otras cosas que he probado:

  • Usando la ordenación por defecto
  • Pasar un char [] en lugar de una cadena (obtener errores de firma del método en su lugar)
¿Fue útil?

Solución 2

Está bien, lo hice funcionar, el problema estaba pasando por la ref. No estoy seguro de por qué, pero esto funciona:

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

Con esta prueba:

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

Otros consejos

... recorte ... OK, lo tengo para trabajar, el problema fue pasando por ref. No estoy seguro de por qué, pero esto funciona: ... recortar ...

Debe pasar por referencia porque esa es la semántica utilizada por el código FORTRAN. El código del cliente pasa un búfer en el que se escribirá el código FORTRAN en lugar de usar un valor de retorno.

... recorte ... ! MS $ ATRIBUTOS DE REFERENCIA :: VRSION ... recortar ...

Este atributo en su código FORTRAN especifica que este parámetro se pasa por referencia. Eso significa que el código FORTRAN va a escribir en esta dirección. Si DllImport no lo declara también como un valor de referencia, obtendrá una infracción de acceso.

¿Has probado con un StringBuilder?

Crea tu String como un StringBuilder y pásalo a la función dll.

No estoy seguro de qué declaración de Marashlling usar, ya que podría funcionar de manera predeterminada.

Eche un vistazo a: Marshal C ++ clase "cadena" en C # P / Invocar

Aquí hay un buen artículo que podría ayudar también: Interop Marshalling

No puedo probar esta solución ya que no tengo un compilador FORTRAN, pero creo que esto funcionaría para usted:

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

Gracias a todos, muchachos, he estado tratando de pasar una cadena de c # a una subrutina de fortran dll y este método fue el único que funcionó entre muchos otros

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top