编组给出错误“尝试读取或写入受保护的内存。这通常表明其他内存已损坏”

问题描述 投票:0回答:1

我需要使用 C 库在我的 C# 项目中处理 NetCDF 文件格式。要从 NetCDF 文件接收部分信息,我需要使用方法 nc_open 打开 CDF 文件,使用 nc_inq_att 获取数组的长度(这个方法工作正常并且没有错误),然后使用 nc_get_att_text 来获取指向字符数组的指针。然后我需要获取这个字符数组并将它们转换为字符串。

UniData 网站上的原始文档描述了 nc_get_att_text

int nc_get_att_text 
(   
int ncid,
int varid,
const char* name,
char* value 
)   

参数 ncid NetCDF 文件或组 ID。 varid 变量 ID,或全局属性的 NC_GLOBAL。 name 属性名称。 value 将获取属性值数组的指针。

我使用这样的代码来导入netcdf.dll

using System;
using System.Runtime.InteropServices;

namespace test
{
    private class LoadData
    {
        private LoadData()
        { 
            OpenFileDialog File = new OpenFileDialog();
            int ncid;
            int errorid;
            string op_name;

            File.Filter = "File .cdf|*.cdf";
            if (File.ShowDialog() == DialogResult.OK)
            {
                //Open CDF file - this one works fine
                errorid = nc_open(File.FileName, CreateMode.NC_NOWRITE, out ncid);

                //inquiring the length of array - this one works fine and gives me correct value (checked with original NetCDF software). attlength.ToInt64() gives result 7.
                errorid = nc_inq_att(ncid, -1, "operator_name", out NcType atttype, out IntPtr attlength);

                //getting pointer works fine
                errorid = nc_get_att_text(ncid, -1, "operator_name", out IntPtr value);

                //sending pointer to array of char and array length to my method
                op_name = CharPointerToString(value, attlength.ToInt64());
            }
        }

        // This method should copy bytes to my bytes array and then make a string from them.
        private string CharPointerToString(IntPtr _pointer, long _length)
        {
            string result;
            char[] chars = new char[(int)_length];
            IntPtr pointer = _pointer;
            Marshal.Copy(_pointer, chars, 0, (int)_length); // here I get Violation "Attempted to read or write protected memory. This is often an indication that other memory is corrupt"
            return result = new string(chars);
        }

        [DllImport("netcdf.dll")]
        public static extern int nc_open(string _FileName, CreateMode _CreateMode, out int ncid);

        [DllImport("netcdf.dll")]
        public static extern int nc_inq_att(int _ncid, int _varid, string _AttributeName, out NcType _type, out IntPtr _AttributeLength);

        [DllImport("netcdf.dll")]
        public static extern int nc_get_att_text(int _ncid, int _varid, string _AttributeName, out IntPtr _value);
    }
}

目前我不知道如何处理这个问题以及导致问题的原因。

我发现在C中,由于ASCII编码,字符只能是一个字节,无论C#中的字符是否是4个字节。我已将函数中的代码替换为准备好字节而不是字符,并使用 ASCII 编码将字节转换为字符串,但这给出了相同的错误。

 private string CharPointerToString(IntPtr _pointer, long _length)
    {
       string result;
       byte[] bytes = new byte[(int)_length];
       Marshal.Copy(_pointer, bytes, 0, (int)_length);
       result = Encoding.ASCII.GetString(bytes);
       return result;
    }

原始库是为x64系统构建的,我也将我的项目构建为x64。我也尝试为任何CPU构建,没有区别。

c# c marshalling dllimport
1个回答
0
投票

因为 StackOverflow 更喜欢我把它写成答案而不是评论(由于缺乏代表,即使评论是它所属的地方......)

(通过一些研究)似乎有一个错误的假设,即 nc_get_att_text() 为您处理内存,为结果单独分配空间。

据我所知,事实并非如此。我不是在谈论其余的实现,但从文档来看,您似乎应该分配一个缓冲区,然后您将指针传递给该函数,而不是它向您传递一个。

要实现此解决方案,您需要更改 P/Invoke 定义以接受而不是发出指针:

[DllImport("netcdf.dll")]
    public static extern int nc_get_att_text(int _ncid, int _varid, string _AttributeName, IntPtr _value);

然后分配内存,然后再传入:

IntPtr buffer = Marshal.AllocHGlobal(attLength + 1);// One more byte to hold trailing null 
int errorid = nc_get_att_text(ncid, -1, "operator_name", buffer);
string op_name = Marshal.PtrToStringAnsi(buffer, attLength);
Marshal.FreeHGlobal(buffer);
© www.soinside.com 2019 - 2024. All rights reserved.