从 C# 调用 FORTRAN DLL 的受保护内存冲突

发布于 2024-07-07 09:05:38 字数 1377 浏览 7 评论 0原文

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

I am trying to call out to a legacy dll compiled from FORTRAN code. I am new to Interop, but I've read some articles on it and it seems like my case should be fairly straightforward.

The method I really want to call has a complex method signature, but I can't even call this simple GetVersion method without getting a protected memory violation.

Here's my DllImport code:

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

Here's the FORTRAN code:

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

Here's my unit test that fails:

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

Here's the error message I get:

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

Other things I've tried:

  • Using the default marshalling
  • Passing a char[] instead of a string (get method signature errors instead)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

凑诗 2024-07-14 09:05:38

...剪断...
好的,我成功了,问题是通过引用传递的。 我不知道为什么,但这有效:
...snip...

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

...剪断...
!MS$属性参考::VRSION
...snip...

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

...snip...
OK, I got it to work, the problem was passing by ref. I'm not sure why, but this works:
...snip...

You need to pass by reference because that is the semantic being used by the FORTRAN code. The client code is passing in a buffer that the FORTRAN code is going to write to in lieu of using a return value.

...snip...
!MS$ATTRIBUTES REFERENCE :: VRSION
...snip...

This attribute in your FORTRAN code specifies that this parameter is passed by reference. That means the FORTRAN code is going to write to this address. If the DllImport doesn't declare it as a ref value also, you will get an access violation.

谁与争疯 2024-07-14 09:05:38

好的,我成功了,问题是通过引用传递的。 我不知道为什么,但这有效:

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

OK, I got it to work, the problem was passing by ref. I'm not sure why, but this works:

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

With this test:

[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);
    }
软糖 2024-07-14 09:05:38

您尝试过使用 StringBuilder 吗?

创建 String 作为 StringBuilder 并将其传递到 dll 函数中。

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

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

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

Have you tried using a StringBuilder?

Create your String as a StringBuilder and pass that into the dll function.

Im unsure as to what Marashlling statement to use, perhapse the default might work.

Have a look at: Marshal C++ “string” class in C# P/Invoke

Heres a good article the might help as well: Interop Marshalling

拿命拼未来 2024-07-14 09:05:38

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

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

I cannot try this solution since I do not have a FORTRAN compiler, but I think this would work for you:

    [DllImport("GeoConvert.dll", 
            EntryPoint="_get_version@4", 
            CallingConvention=CallingConvention.StdCall,
            CharSet=CharSet.Ansi)]
    public static extern void GetGeoConvertVersion(StringBuilder version);
鲜肉鲜肉永远不皱 2024-07-14 09:05:38

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

Thank you all guys, I've been trying to pass a string from c# to a subroutine from fortran dll and this method was the only working one among lots of others

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文