最近一直在研究。Net Micro Framework字体文件(tinyfnt),由于tinyfnt文件头部有一段描述数据,所以很想定义一个结构体,像VC一样直接从文件中读出来,省得用流一个个解析很是麻烦。
没有想到在C#中竟没有直接的指令,想必C#设计者认为提供了流和序列化技术,一切问题都可以迎刃而解了。
C#语言的结构体是一个比较复杂的东西,在此之上有很多需要设置的参数,否则用起来就很容易出错。下面是msdn上一段描述,看看也许有助于理解C#语言的结构体。
通过使用属性可以自定义结构在内存中的布局方式。例如,可以使用 StructLayout(LayoutKind.Explicit) 和 FieldOffset 属性创建在 C/C++ 中称为联合的布局。
- [System.Runtime.InteropServices.StructLayout(LayoutKind.Explicit)]
- struct TestUnion
- {
- [System.Runtime.InteropServices.FieldOffset(0)]
- public int i;
- [System.Runtime.InteropServices.FieldOffset(0)]
- public double d;
- [System.Runtime.InteropServices.FieldOffset(0)]
- public char c;
- [System.Runtime.InteropServices.FieldOffset(0)]
- public byte b;
- }
在上一个代码段中,TestUnion 的所有字段都从内存中的同一位置开始。
以下是字段从其他显式设置的位置开始的另一个示例。
- [System.Runtime.InteropServices.StructLayout(LayoutKind.Explicit)]
- struct TestExplicit
- {
- [System.Runtime.InteropServices.FieldOffset(0)]
- public long lg;
- [System.Runtime.InteropServices.FieldOffset(0)]
- public int i1;
- [System.Runtime.InteropServices.FieldOffset(4)]
- public int i2;
- [System.Runtime.InteropServices.FieldOffset(8)]
- public double d;
- [System.Runtime.InteropServices.FieldOffset(12)]
- public char c;
- [System.Runtime.InteropServices.FieldOffset(14)]
- public byte b;
- }
i1 和 i2 这两个 int 字段共享与 lg 相同的内存位置。使用平台调用时,这种结构布局控制很有用。
我做了一个简单的测试程序,基本达成预定需求,不过程序该方式要求比较苛刻,如果要解析的数据与转换C#语言的结构体不匹配就会引发一系列莫名其妙的异常(如内存不可读等等之类),下面是测试程序的源代码,有兴趣的朋友可以看一看,也希望网友能提出更好的方案。
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Data;
- using System.Drawing;
- using System.Text;
- using System.Windows.Forms;
- using System.IO;
- using System.Runtime.InteropServices;
- namespace RWFile
- {
- public partial class Form1 : Form
- {
- public Form1()
- {
- InitializeComponent();
- }
- //从文件中读结构体
- private void button1_Click(object sender, EventArgs e)
- {
- string strFile = Application.StartupPath + "\\test.dat";
- if (!File.Exists(strFile))
- {
- MessageBox.Show("文件不存在");
- return;
- }
- FileStream fs = new FileStream(strFile, FileMode.Open,
- FileAccess.ReadWrite);
- TestStruct ts = new TestStruct();
- byte[] bytData = new byte[Marshal.SizeOf(ts)];
- fs.Read(bytData, 0, bytData.Length);
- fs.Close();
- ts = rawDeserialize(bytData);
- textBox1.Text = ts.dTest.ToString();
- textBox2.Text = ts.uTest.ToString();
- textBox3.Text = Encoding.Default.GetString(ts.bTest);
- }
- //向文件中写结构体
- private void button2_Click(object sender, EventArgs e)
- {
- string strFile = Application.StartupPath + "\\test.dat";
- FileStream fs = new FileStream(strFile, FileMode.Create ,
- FileAccess.Write);
- TestStruct ts = new TestStruct();
- ts.dTest = double.Parse(textBox1.Text);
- ts.uTest = UInt16.Parse(textBox2.Text);
- ts.bTest = Encoding.Default.GetBytes(textBox3.Text);
- byte[] bytData = rawSerialize(ts);
- fs.Write(bytData, 0, bytData.Length);
- fs.Close();
- }
- [StructLayout(LayoutKind.Sequential,CharSetCharSet = CharSet.Ansi)] //,Size=16
- public struct TestStruct
- {
- [MarshalAs(UnmanagedType.R8)] //,FieldOffset(0)]
- public double dTest;
- [MarshalAs(UnmanagedType.U2)] //, FieldOffset(8)]
- public UInt16 uTest;
- [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
- //, FieldOffset(10)]
- public byte[] bTest;
- }
- //序列化
- public static byte[] rawSerialize(object obj)
- {
- int rawsize = Marshal.SizeOf(obj);
- IntPtr buffer = Marshal.AllocHGlobal(rawsize);
- Marshal.StructureToPtr(obj, buffer, false);
- byte[] rawdatas = new byte[rawsize];
- Marshal.Copy(buffer, rawdatas, 0, rawsize);
- Marshal.FreeHGlobal(buffer);
- return rawdatas;
- }
- //反序列化
- public static TestStruct rawDeserialize(byte[] rawdatas)
- {
- Type anytype = typeof(TestStruct);
- int rawsize = Marshal.SizeOf(anytype);
- if (rawsize > rawdatas.Length) return new TestStruct();
- IntPtr buffer = Marshal.AllocHGlobal(rawsize);
- Marshal.Copy(rawdatas, 0, buffer, rawsize);
- object retobj = Marshal.PtrToStructure(buffer, anytype);
- Marshal.FreeHGlobal(buffer);
- return (TestStruct)retobj;
- }
- }
- }
【编辑推荐】