4

我正在尝试调用从 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[] 而不是字符串(改为获取方法签名错误)
4

5 回答 5

3

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

您需要通过引用传递,因为这是 FORTRAN 代码使用的语义。客户端代码正在传递一个缓冲区,FORTRAN 代码将写入该缓冲区以代替使用返回值。

...snip... !MS$ATTRIBUTES 参考 :: VRSION ...snip...

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

于 2008-10-23T17:56:58.817 回答
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);
    }
于 2008-10-23T12:22:49.833 回答
0

您是否尝试过使用 StringBuilder?

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

我不确定要使用什么 Marashlling 语句,也许默认值可能会起作用。

看看:C# P/Invoke 中的 Marshal C++ “string” 类

这是一篇可能也有帮助的好文章:Interop Marshalling

于 2008-10-23T12:13:56.920 回答
0

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

    [DllImport("GeoConvert.dll", 
            EntryPoint="_get_version@4", 
            CallingConvention=CallingConvention.StdCall,
            CharSet=CharSet.Ansi)]
    public static extern void GetGeoConvertVersion(StringBuilder version);
于 2008-10-23T14:14:32.917 回答
0

谢谢大家,我一直在尝试将字符串从 c# 传递给 fortran dll 的子例程,这种方法是许多其他方法中唯一有效的方法

于 2011-02-11T06:48:01.527 回答