Нарушение защищенной памяти при вызове библиотеки FORTRAN DLL из C # - PullRequest
4 голосов
/ 23 октября 2008

Я пытаюсь вызвать устаревшую dll, скомпилированную из кода FORTRAN. Я новичок в Interop, но я прочитал несколько статей об этом, и похоже, что мой случай должен быть довольно простым.

Метод, который я действительно хочу вызвать, имеет сложную сигнатуру метода, но я даже не могу вызвать этот простой метод GetVersion без получения нарушения защищенной памяти.

Вот мой код DllImport:

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

Вот код ФОРТРАНА:

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

Вот мой модульный тест, который не прошел:

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

Вот сообщение об ошибке, которое я получаю:

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

Другие вещи, которые я пробовал:

  • Использование маршаллинга по умолчанию
  • Передача char [] вместо строки (вместо этого получите ошибки подписи метода)

Ответы [ 5 ]

3 голосов
/ 23 октября 2008

... чик ... ОК, я получил его на работу, проблема проходила мимо реф. Я не уверен, почему, но это работает: ... чик ...

Вам нужно перейти по ссылке, потому что это семантика, используемая кодом FORTRAN. Код клиента передается в буфер, в который будет записываться код FORTRAN вместо использования возвращаемого значения.

... чик ... ! MS $ АТРИБУТЫ ССЫЛКА :: VRSION ... чик ...

Этот атрибут в вашем коде FORTRAN указывает, что этот параметр передается по ссылке. Это означает, что код FORTRAN будет записываться по этому адресу. Если DllImport также не объявляет его как значение ref, вы получите нарушение прав доступа.

1 голос
/ 23 октября 2008

ОК, я получил его на работу, проблема проходила мимо реф. Я не уверен почему, но это работает:

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

С этим тестом:

[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);
    }
0 голосов
/ 11 февраля 2011

Спасибо всем, ребята, я пытался передать строку из c # в подпрограмму из fortran dll, и этот метод был единственным работающим среди множества других

0 голосов
/ 23 октября 2008

Я не могу попробовать это решение, так как у меня нет компилятора FORTRAN, но я думаю, что это будет работать для вас:

    [DllImport("GeoConvert.dll", 
            EntryPoint="_get_version@4", 
            CallingConvention=CallingConvention.StdCall,
            CharSet=CharSet.Ansi)]
    public static extern void GetGeoConvertVersion(StringBuilder version);
0 голосов
/ 23 октября 2008

Вы пытались использовать StringBuilder?

Создайте свою String как StringBuilder и передайте ее в функцию dll.

Я не уверен относительно того, какой оператор Marashlling использовать, возможно, будет полезен вариант по умолчанию.

Взгляните на: Маршал C ++ «строковый» класс в C # P / Invoke

Вот хорошая статья, которая также может помочь: Interop Marshalling

...