有人能告诉我如何在C#.NET版本2中以直接方式将字节数组添加到结构中吗?就像在C中找到的熟悉的fread
一样,到目前为止,我在读取字节流并自动填充结构方面没有取得多大成功。我已经看到了一些使用unsafe
关键字在托管代码中存在指针hocus-pocus的实现。
看一下这个样本:
public unsafe struct foobarStruct{
/* fields here... */
public foobarStruct(int nFakeArgs){
/* Initialize the fields... */
}
public foobarStruct(byte[] data) : this(0) {
unsafe {
GCHandle hByteData = GCHandle.Alloc(data, GCHandleType.Pinned);
IntPtr pByteData = hByteData.AddrOfPinnedObject();
this = (foobarStruct)Marshal.PtrToStructure(pByteData, this.GetType());
hByteData.Free();
}
}
}
我在foobarStruct
这种实现是否足够好,还是有更清洁的方法来实现这一目标?
编辑:我不想使用ISerializable接口或其实现。 我正在尝试读取二进制图像以计算出使用的字段并使用PE结构确定其数据。
答案 0 :(得分:10)
使用P / Invoke编组器没有任何问题,它不是不安全的,您不必使用unsafe关键字。出错只会产生不良数据。它可以比显式编写反序列化代码更容易使用,尤其是当文件包含字符串时。您不能使用BinaryReader.ReadString(),它假定该字符串是由BinaryWriter编写的。但是请确保使用结构声明声明数据的结构,this.GetType()不太可能正常运行。
这是一个通用类,可以使它适用于任何结构声明:
class StructureReader<T> where T : struct {
private byte[] mBuffer;
public StructureReader() {
mBuffer = new byte[Marshal.SizeOf(typeof(T))];
}
public T Read(System.IO.FileStream fs) {
int bytes = fs.Read(mBuffer, 0, mBuffer.Length);
if (bytes == 0) throw new InvalidOperationException("End-of-file reached");
if (bytes != mBuffer.Length) throw new ArgumentException("File contains bad data");
T retval;
GCHandle hdl = GCHandle.Alloc(mBuffer, GCHandleType.Pinned);
try {
retval = (T)Marshal.PtrToStructure(hdl.AddrOfPinnedObject(), typeof(T));
}
finally {
hdl.Free();
}
return retval;
}
文件中数据结构的示例声明:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
struct Sample {
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 42)]
public string someString;
}
您需要调整结构声明和属性以获得与文件中数据的匹配。读取文件的示例代码:
var data = new List<Sample>();
var reader = new StructureReader<Sample>();
using (var stream = new FileStream(@"c:\temp\test.bin", FileMode.Open, FileAccess.Read)) {
while(stream.Position < stream.Length) {
data.Add(reader.Read(stream));
}
}
答案 1 :(得分:3)
您可能希望使用BinaryReader
,它允许您以二进制形式读取基本类型。
从byte[]
创建MemoryStream
,然后使用BinaryReader
。您应该能够读出结构并相应地填写您的对象。