关于C++结构体与C#结构体对应,以及解析BLOB块
第一部分
需求
- c++结构体数据以二进制形式写入Oracle blob 中,
- c# 读取对应blob,解析成对应结构体。
- 存储的blob内容为一张表,记录了不同location 处,多个属性的值
| location | filed_1 | filed_2 | … | field_7 |
| value | filed_value | filed_value | … | field_value |
| location | filed_1 | filed_2 | … | field_7 |
#pragma pack(1)
struct BlobData
{
short h[10];
char ColumName[动态行数][40];
double CurveData[动态行数][动态列数];
};
#pragma pack()
[StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public struct RECORDS_DATA
{
public int Cols;
public int Rows;
public int FieldsOffset;
public int DataOffset;
public int BufferSizetelte;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 512 * 1024)]
public byte[] Data;
}
问题
- c++ 和 c# 数据类型的对应;
- c# 如何将 blob 解析为 结构体?
类型对应
看上述代码:
c++ c#
int int
long int
double double
解析
static void Main(string[] args)
{
string constring = string.Concat(
@"Data Source=",
@" (DESCRIPTION=",
@" (ADDRESS_LIST=",
@" (ADDRESS=",
@" (PROTOCOL=TCP)",
@" (HOST=127.0.0.1)",
@" (PORT=1521)",
@" )",
@" )",
@" (CONNECT_DATA=",
@" (SERVICE_NAME=服务名)",
@" )",
@" );",
@"Persist Security Info=True;",
@"User Id=账户;",
@"Password=密码"
);
OracleConnection conn = new OracleConnection(constring);
try
{
conn.Open();
OracleCommand com = conn.CreateCommand();
com.CommandText = "SELECT blob_data from table WHERE xxx";
OracleDataReader odr = com.ExecuteReader();
while (odr.Read())
{
#region 获取blob数据
byte[] binary_data = (byte[])odr.GetOracleLob(0).Value;
int len = Marshal.SizeOf(typeof(RECORDS_DATA));
int act_len = binary_data.Length;
unsafe
{
Console.WriteLine("len: " + len);
Console.WriteLine("binary_data: " + act_len);
IntPtr p = Marshal.AllocHGlobal(len);
Marshal.Copy(binary_data, 0, p, act_len);
var dbh = (RECORDS_DATA)Marshal.PtrToStructure(p, typeof(RECORDS_DATA));
var colNum = dbh.Cols;
var rowNum = dbh.Rows;
Console.WriteLine("Cols: " + dbh.Cols);
Console.WriteLine("Rows: " + dbh.Rows);
Console.WriteLine("DataOffset: " + dbh.DataOffset);
Console.WriteLine("FieldsOffset: " + dbh.FieldsOffset);
Console.WriteLine("BufferSizetelte: " + dbh.BufferSizetelte);
#endregion
#region 解析表头
var da = dbh.Data;
var field_begin = (char*)(IntPtr.Add(p, dbh.FieldsOffset));
Console.WriteLine("print filed******************************************");
for (int col = 0; col < dbh.Cols; col++)
{
string filed_name = Marshal.PtrToStringAnsi(IntPtr.Add(p, dbh.FieldsOffset + 40 * col));
Console.WriteLine("filed_name: " + filed_name);
short filed_type = Marshal.ReadInt16(IntPtr.Add(p, dbh.FieldsOffset + 40 * col), 32);
Console.WriteLine("filed_type: " + filed_type);
short filed_size = Marshal.ReadInt16(IntPtr.Add(p, dbh.FieldsOffset + 40 * col), 32 + 2);
Console.WriteLine("filed_size: " + filed_size);
int filed_offset = Marshal.ReadInt32(IntPtr.Add(p, dbh.FieldsOffset + 40 * col), 32 + 2 + 2);
Console.WriteLine("filed_offset: " + filed_offset);
}
Console.WriteLine("*****************************************************");
#endregion
#region 解析数据体
var data_begin = (IntPtr.Add(p, dbh.DataOffset));
int startIndex = 0;
int length = dbh.Rows * dbh.Cols;
double[,] dst = new double[dbh.Rows, dbh.Cols];
double[] src = new double[dbh.Rows * dbh.Cols];
Marshal.Copy(data_begin, src, startIndex, length);
for (int i = 0; i < rowNum; ++i)
{
for (int j = 0; j < colNum; ++j)
{
dst[i, j] = src[i * colNum + j];
}
}
for (int row = 0; row < rowNum; ++row)
{
Console.WriteLine("location: " + dst[row, 0] + " ,thinkness: " + dst[row, 1]);
}
#endregion
Marshal.FreeHGlobal(p);
}
}
odr.Close();
}
catch (Exception ex)
{
Console.WriteLine("error: " + ex.Message);
}
finally
{
conn.Close();
}
总结
- 刚接触c#的托管内存,感觉用起来不舒服,后来理清思路,还算可以,但感觉还是c++好用。
- 存在一个问题,如何直接将数据内存映射到结构体中, 暂时思路就是将二维数据读入一维数组中,然后手动转为二维数据。
有兴趣请关注公众号: