受保护的内存违规从C#调用FORTRAN DLL

时间:2008-10-23 11:18:41

标签: c# .net interop fortran dllimport

我正在尝试调用从FORTRAN代码编译的遗留DLL。我是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);

这是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

这是我的单元测试失败:

[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 个答案:

答案 0 :(得分:3)

... ...剪断 好的,我让它工作,问题是通过ref。我不知道为什么,但这有效: ... ...剪断

您需要通过引用传递,因为这是FORTRAN代码使用的语义。客户端代码传入FORTRAN代码要写入的缓冲区,而不是使用返回值。

... ...剪断 !MS $ ATTRIBUTES REFERENCE :: VRSION ... ...剪断

FORTRAN代码中的此属性指定此参数通过引用传递。这意味着FORTRAN代码将写入此地址。如果DllImport也没有将其声明为ref值,您将获得访问冲突。

答案 1 :(得分:1)

好的,我得到它的工作,问题是通过参考。我不知道为什么,但这有效:

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

答案 2 :(得分:0)

您是否尝试过使用StringBuilder?

将String创建为StringBuilder并将其传递给dll函数。

我不确定使用什么Marashlling语句,默认可能会有效。

查看:Marshal C++ “string” class in C# P/Invoke

这是一篇可能有用的好文章:Interop Marshalling

答案 3 :(得分:0)

我无法尝试此解决方案,因为我没有FORTRAN编译器,但我认为这对您有用:

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

答案 4 :(得分:0)

谢谢大家,我一直在尝试将一个字符串从c#传递给fortran dll的子程序,这个方法是其他很多人中唯一的工作方法