我有一个C程序,其中定义了以下结构:
typedef struct{
DWORD id;
char name[256];
}ROW;
typedef struct{
DWORD Prev;
DWORD Next;
WORD ItemCount;
struct ROW Item[256];
} PAGE;
...在某些时候,程序会填充 PAGE 结构并将其写入磁盘文件。
现在,我需要从C#程序中读取这个文件,但是我无法弄清楚如何在C#中定义一个等价的类,我尝试的所有内容都无法编译或抛出异常。在C语言中,这是一项非常简单的任务,很可能在C#中也是如此,但是我是C#的新手,并且还没有找到关于如何正确完成它的明确解释。
答案 0 :(得分:1)
我个人在这里使用BinaryReader
。原因是它允许您定义一组类型来表示这些结构,在C#中感觉干净。如果您使用Marshal.PtrToStructure
,那么您将被迫定义在C#中感觉笨重的互操作类型。
你声明结构包装好了。对齐的结构使生活更加艰难,因为您必须了解编译器如何布局结构。为了存储到磁盘,通常应该避免使用对齐的结构。
我可能会定义这样的类型:
public struct Row
{
public uint id;
public string name;
}
public struct Page
{
public uint prev;
public uint next;
public Row[] items;
}
如果您愿意,可以使用课程。或List<Row>
。这完全取决于你。
然后使用如下方法读取文件:
public static Page ReadPage(BinaryReader reader)
{
Page page;
page.prev = reader.ReadUInt32();
page.next = reader.ReadUInt32();
ushort count = reader.ReadUInt16();
page.items = new Row[count];
for (int i=0; i<count; i++)
{
page.items[i].id = reader.ReadUInt32();
page.items[i].name = Encoding.ASCII.GetString(reader.ReadBytes(256));
}
// skip past the unused rows
reader.ReadBytes((256+sizeof(uint))*(256-count));
}
我假设是ASCII编码。但也许它是UTF-8或ANSI。我假设字符串是空终止的,但实际上没有编码任何空终止符检测。我希望你能做到这一点!
答案 1 :(得分:0)
请注意,这在C中存在风险 - 您无法保证不会使用其他字节填充结构。 (好吧,不是没有一些特定于编译器的标志。)
StructLayout
属性允许您定义结构的布局方式:
[StructLayout(LayoutKind.Sequential)]
public struct Point
{
public int x;
public int y;
}
这将x
然后y
。如果要强制进行字节对齐,可以执行以下操作:
[StructLayout(LayoutKind.Sequential,Pack=1)]
public struct Point
{
public int x;
public int y;
}
(在这种情况下无关紧要,但可能与其他项目有关)。您还可以定义每个字段的确切布局:
[StructLayout(LayoutKind.Explicit)]
public struct Rect
{
[FieldOffset(0)] public int left;
[FieldOffset(4)] public int top;
[FieldOffset(8)] public int right;
[FieldOffset(12)] public int bottom;
}
然后您应该能够使用Marshal.PtrToStructure
读取原始字节并在其上覆盖结构。我没有方便的示例代码(自从我完成它以来已有几年),但它确实有效。
答案 2 :(得分:0)
您必须使用[StructLayout-Attribute](http://msdn.microsoft.com/de-de/library/system.runtime.interopservices.structlayoutattribute%28v=vs.110%29.aspx),例如:
[StructLayout(LayoutKind.Sequential, Pack = ?, Size = ??)]
public struct SomeStruct
{
public ushort wYear;
public ushort second;
public ushort third;
...
}
有一天,我创建了一个通用的静态方法来将流读入结构:
public static T StreamToStruct<T>(Stream stream, int offset = 0) where T : struct
{
Type structType = typeof(T);
if(structType.StructLayoutAttribute.Value != LayoutKind.Sequential)
{
throw new ArgumentException("structType isn't a struct or the layout isn't sequential.");
}
int tmpSize = Marshal.SizeOf(structType);
Byte[] tmp = new Byte[tmpSize];
stream.Read(tmp, offset, tmpSize);
GCHandle structHandle = GCHandle.Alloc(tmp, GCHandleType.Pinned);
try
{
T structure = (T)Marshal.PtrToStructure(structHandle.AddrOfPinnedObject(), structType);
return structure;
}
catch(Exception)
{
throw;
}
finally
{
structHandle.Free();
}
}