从字节数组中读取 C# 中的 C/C++ 数据结构

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2871/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-07-31 13:33:03  来源:igfitidea点击:

Reading a C/C++ data structure in C# from a byte array

提问by Chris Miller

What would be the best way to fill a C# struct from a byte[] array where the data was from a C/C++ struct? The C struct would look something like this (my C is very rusty):

从数据来自 C/C++ 结构的 byte[] 数组填充 C# 结构的最佳方法是什么?C 结构看起来像这样(我的 C 非常生疏):

typedef OldStuff {
    CHAR Name[8];
    UInt32 User;
    CHAR Location[8];
    UInt32 TimeStamp;
    UInt32 Sequence;
    CHAR Tracking[16];
    CHAR Filler[12];
}

And would fill something like this:

并会填充这样的东西:

[StructLayout(LayoutKind.Explicit, Size = 56, Pack = 1)]
public struct NewStuff
{
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)]
    [FieldOffset(0)]
    public string Name;

    [MarshalAs(UnmanagedType.U4)]
    [FieldOffset(8)]
    public uint User;

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)]
    [FieldOffset(12)]
    public string Location;

    [MarshalAs(UnmanagedType.U4)]
    [FieldOffset(20)]
    public uint TimeStamp;

    [MarshalAs(UnmanagedType.U4)]
    [FieldOffset(24)]
    public uint Sequence;

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)]
    [FieldOffset(28)]
    public string Tracking;
}

What is best way to copy OldStuffto NewStuff, if OldStuffwas passed as byte[] array?

如果作为 byte[] 数组传递,复制OldStuff到的最佳方法是什么?NewStuffOldStuff

I'm currently doing something like the following, but it feels kind of clunky.

我目前正在做类似以下的事情,但感觉有点笨拙。

GCHandle handle;
NewStuff MyStuff;

int BufferSize = Marshal.SizeOf(typeof(NewStuff));
byte[] buff = new byte[BufferSize];

Array.Copy(SomeByteArray, 0, buff, 0, BufferSize);

handle = GCHandle.Alloc(buff, GCHandleType.Pinned);

MyStuff = (NewStuff)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(NewStuff));

handle.Free();

Is there better way to accomplish this?

有没有更好的方法来实现这一点?



Would using the BinaryReaderclass offer any performance gains over pinning the memory and using Marshal.PtrStructure?

使用BinaryReader该类会比固定内存和使用 提供任何性能提升Marshal.PtrStructure吗?

采纳答案by Coincoin

From what I can see in that context, you don't need to copy SomeByteArrayinto a buffer. You simply need to get the handle from SomeByteArray, pin it, copy the IntPtrdata using PtrToStructureand then release. No need for a copy.

从我在这种情况下看到的情况来看,您不需要复制SomeByteArray到缓冲区中。您只需要从 获取句柄SomeByteArray,固定它,IntPtr使用复制数据PtrToStructure然后释放。不需要副本。

That would be:

那将是:

NewStuff ByteArrayToNewStuff(byte[] bytes)
{
    GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
    try
    {
        NewStuff stuff = (NewStuff)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(NewStuff));
    }
    finally
    {
        handle.Free();
    }
    return stuff;
}

Generic version:

通用版本:

T ByteArrayToStructure<T>(byte[] bytes) where T: struct 
{
    T stuff;
    GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
    try
    {
        stuff = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
    }
    finally
    {
        handle.Free();
    }
    return stuff;
}

Simpler version (requires unsafeswitch):

更简单的版本(需要unsafe开关):

unsafe T ByteArrayToStructure<T>(byte[] bytes) where T : struct
{
    fixed (byte* ptr = &bytes[0])
    {
        return (T)Marshal.PtrToStructure((IntPtr)ptr, typeof(T));
    }
}

回答by Mufaka

If you have a byte[] you should be able to use the BinaryReader class and set values on NewStuff using the available ReadX methods.

如果您有一个 byte[],您应该能够使用 BinaryReader 类并使用可用的 ReadX 方法在 NewStuff 上设置值。

回答by Tim Ring

Watch out for packing issues. In the example you gave all fields are at the obvious offsets because everything is on 4 byte boundaries but this will not always be the case. Visual C++ packs on 8 byte boundaries by default.

注意包装问题。在您给出的示例中,所有字段都在明显的偏移量处,因为所有内容都在 4 字节边界上,但情况并非总是如此。默认情况下,Visual C++ 打包在 8 字节边界上。

回答by Dushyant

object ByteArrayToStructure(byte[] bytearray, object structureObj, int position)
{
    int length = Marshal.SizeOf(structureObj);
    IntPtr ptr = Marshal.AllocHGlobal(length);
    Marshal.Copy(bytearray, 0, ptr, length);
    structureObj = Marshal.PtrToStructure(Marshal.UnsafeAddrOfPinnedArrayElement(bytearray, position), structureObj.GetType());
    Marshal.FreeHGlobal(ptr);
    return structureObj;
}   

Have this

有这个

回答by cdiggins

Here is an exception safe version of the accepted answer:

这是已接受答案的异常安全版本:

public static T ByteArrayToStructure<T>(byte[] bytes) where T : struct
{
    var handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
    try {
        return (T) Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
    }
    finally {
        handle.Free();
    }
}