我需要使用 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构建,没有区别。
因为 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);