Pergunta

Eu estou tentando chamar a uma dll legado compilado a partir do código Fortran. Eu sou novo para Interop, mas eu li alguns artigos sobre isso e parece que meu caso deve ser bastante simples.

O método que eu realmente quero chamada tem uma assinatura de método complexa, mas eu não posso nem chamar esse método GetVersion simples sem receber uma violação de memória protegido.

Aqui está o meu código de DllImport:

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

Aqui está o 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

Aqui está o meu teste de unidade que falha:

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

Aqui está a mensagem de erro que eu recebo:

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

Outras coisas que eu tentei:

  • Usando a triagem padrão
  • Passando um char [] em vez de uma string (obter erros de assinatura método em vez)
Foi útil?

Solução 2

OK, eu tenho que trabalhar, o problema estava passando ref. Eu não sei por que, mas isso funciona:

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

Com este teste:

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

Outras dicas

... snip ... OK, eu tenho que trabalhar, o problema estava passando ref. Eu não sei por que, mas isso funciona: ... snip ...

Você precisa passar por referência porque é o ser semântica usada pelo código Fortran. O código do cliente está passando em um buffer que o código FORTRAN vai escrever para em vez de usar um valor de retorno.

... snip ... ! MS $ atributos de referência :: VRSION ... snip ...

Este atributo em seus especifica código do Fortran que este parâmetro é passado por referência. Isso significa que o código FORTRAN vai escrever para este endereço. Se o DllImport não declará-la como um valor ref também, você vai ter uma violação de acesso.

Você já tentou usar um StringBuilder?

Crie a sua String como um StringBuilder e passar isso para a função de dll.

Im inseguro quanto ao que declaração Marashlling para uso, perhapse o trabalho poder padrão.

Tenha um olhar em: Marshal C ++ “string” classe em C # P / Invoke

Aqui está um bom artigo a força ajuda, bem como: Interop Marshalling

Eu não posso experimentar esta solução, uma vez que eu não tenho um compilador Fortran, mas acho que isso iria trabalhar para você:

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

Obrigado todos os caras, eu tenho tentado passar uma cadeia de c # para uma sub-rotina de dll fortran e este método foi a única trabalhando entre muitos outros

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top