将二进制数据加载到结构体中

3
我正在尝试用从byte[]加载的数据填充一个结构(不一定是实际的结构)。
byte[]中有许多不同的数据结构,其中之一是字符串,声明如下:
UInt16 stringLenght
byte[stringLenght] zeroTerminatedString

“C”语言中可以通过声明一个固定大小的结构体来处理这个问题,而不是让结构体包含实际的字符串,而是创建一个指向该字符串的指针。
类似于:
```c struct example { int id; char *string_ptr; }; ```
UInt16 stringLength
char* zeroTerminatedString

有没有一种(聪明的)方法在C#中实现类似的操作?我的意思是从文件/内存中加载二进制数据并将其填充到一个结构中?祝好,Jakob Justesen。

2
你提出的 C 代码有缺陷,所以你将其转换为 C# 的机会注定会失败! - Phil J Pearson
这要看情况。有多少数据记录?它们有多大? - Prof. Falken
2个回答

4

这不是在C语言中声明结构体的方式。如果文件中的记录包含字符串,则可以类似地声明结构体:

struct Example {
    int mumble;   // Anything, not necessarily a string length
    char text[42];
    // etc...
};

相应的C#声明将如下所示:

    [StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
    private struct Example {
        public int mumble;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 42)]
        public string text;
        // etc...
    }

通常情况下,您可以使用BinaryReader读取数据。 但是它不能直接处理这样的字符串,您必须将它们作为byte[]读取并自己进行字符串转换。 您也无法利用声明性语法,您必须为结构体的每个成员编写一个调用。

有一个解决方法,Marshal类已经知道如何使用PtrToStructure()方法将非托管结构转换为托管结构。 这里有一个通用实现,它适用于任何可平移类型。 两个版本,一个静态版本从byte[]中读取,另一个实例方法被优化为重复从流中读取。 您可以使用FileStream或MemoryStream来实现。

using System;
using System.IO;
using System.Runtime.InteropServices;

class StructTranslator {
    public static bool Read<T>(byte[] buffer, int index, ref T retval) {
        if (index == buffer.Length) return false;
        int size = Marshal.SizeOf(typeof(T));
        if (index + size > buffer.Length) throw new IndexOutOfRangeException();
        var handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
        try {
            IntPtr addr = (IntPtr)((long)handle.AddrOfPinnedObject() + index);
            retval = (T)Marshal.PtrToStructure(addr, typeof(T));
        }
        finally {
            handle.Free();
        }
        return true;
    }

    public bool Read<T>(Stream stream, ref T retval) {
        int size = Marshal.SizeOf(typeof(T));
        if (buffer == null || size > buffer.Length) buffer = new byte[size];
        int len = stream.Read(buffer, 0, size);
        if (len == 0) return false;
        if (len != size) throw new EndOfStreamException();
        var handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
        try {
            retval = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
        }
        finally {
            handle.Free();
        }
        return true;
    }

    private byte[] buffer;
}

未经测试,希望它能正常工作。


0

Marshal 应该能够为您完成此操作。

请注意,这只能对结构体进行操作,您可能需要使用 StructLayout 属性。

我不确定如何处理字符串或数组,但是 BStrWrapperArrayWithOffset 可能会有所帮助,还要密切关注类/属性的相似之处(我知道我以前曾经做过将其绑定到本地函数的工作)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接