如何从C#读取C程序生成的数据文件?

时间:2014-02-12 15:09:02

标签: c# c class struct

我有一个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#的新手,并且还没有找到关于如何正确完成它的明确解释。

3 个答案:

答案 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;
} 

(来自MSDN的示例:http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.structlayoutattribute%28v=vs.110%29.aspx

然后您应该能够使用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();
        }
    }