C# 编码问题

标签 c# marshalling

根据 svick 的建议,我相信我可以大大简化我的帖子和问题。下面是一些完整的代码,演示了我的问题和问题,即将字节编码到结构并没有按照我期望的方式工作...... 对于具有由另一个基元分隔的两个数组的对象,我的对象不会按照我期望的方式编码为字节。虽然我指定了“Sequential”,但两个 byte[] 数组首先放入字节数组中,后面是 uint。这是怎么回事? 在“现实生活”中,我正在处理来自其他人的二进制文件,其中数据实际上按字节[5]firstArray,uintfirmwareVersion,byte[9]secondArray的顺序排列。

根据要求,我提供了带有注释的完整代码示例。

namespace MarshallingExample
{
class Program
{
    static void Main(string[] args)
    {

        // first show that our Marshalling to/from objects to bytes is working ...
        Simple simpleObject = new Simple();
        simpleObject.HeaderInfo = new byte[5] { 1, 2, 3, 4, 5 };
        simpleObject.FirmwareRevision = 1234;
        byte[] simpleObjectBytes = Tools.ConvertObjectToBytes(simpleObject);
        Simple simpleObject2 = Tools.ConvertBytesToObject(simpleObjectBytes, 0, typeof(Simple)) as Simple;

        Complex complexObject = new Complex();
        complexObject.HeaderInfo = new byte[5] { 1, 2, 3, 4, 5 };
        complexObject.FirmwareRevision = 1234;

        complexObject.SoftwarePartNumber = new byte[9] { 11, 12, 13, 14, 15, 16, 17, 18, 19 };
        byte[] complexObjectBytes = Tools.ConvertObjectToBytes(complexObject);   // look at complexObjectBytes!!!
        // Notice that the two arrays come first in complexObjectBytes.  Not a problem here.
        Complex complexObject2 = Tools.ConvertBytesToObject(complexObjectBytes, 0, typeof(Complex)) as Complex;



        // Set up some data in MemoryStream(would really be in files) as it's actually given to us....
        MemoryStream memStreamSimple;
        memStreamSimple = new MemoryStream(9);
        memStreamSimple.Write(simpleObject.HeaderInfo, 0, 5);
        memStreamSimple.Write(BitConverter.GetBytes(simpleObject.FirmwareRevision), 0, 4);

        MemoryStream memStreamComplex;
        memStreamComplex = new MemoryStream(18);
        memStreamComplex.Write(complexObject.HeaderInfo, 0, 5);
        memStreamComplex.Write(BitConverter.GetBytes(complexObject.FirmwareRevision), 0, 4);
        memStreamComplex.Write(complexObject.SoftwarePartNumber, 0, 9);


        // now read and try to put in our structures....

        memStreamSimple.Seek(0, SeekOrigin.Begin);
        simpleObjectBytes = new byte[9];
        int count = memStreamSimple.Read(simpleObjectBytes, 0, 9);
        simpleObject2 = Tools.ConvertBytesToObject(simpleObjectBytes, 0, typeof(Simple)) as Simple;

        memStreamComplex.Seek(0, SeekOrigin.Begin);
        complexObjectBytes = new byte[18];
        count = memStreamComplex.Read(complexObjectBytes, 0, 18);
        // Note that following object is all messed up, probably because it was expecting the two arrays first.
        complexObject2 = Tools.ConvertBytesToObject(complexObjectBytes, 0, typeof(Complex)) as Complex;
    }
}

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public class Simple
{
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
    private byte[] _headerInfo = new byte[5];
    public byte[] HeaderInfo
    {
        get
        {
            return _headerInfo;
        }
        set
        {
            _headerInfo = value;
        }
    }

    public uint FirmwareRevision
    {
        get;
        set;
    }


}

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public class Complex
{
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
    private byte[] _headerInfo = new byte[5];
    public byte[] HeaderInfo
    {
        get
        {
            return _headerInfo;
        }
        set
        {
            _headerInfo = value;
        }
    }

    public uint FirmwareRevision
    {
        get;
        set;
    }

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 9)]
    private byte[] _softwarePartNumber = new byte[9];
    public byte[] SoftwarePartNumber
    {
        get
        {
            return _softwarePartNumber;
        }
        set
        {
            _softwarePartNumber = value;
        }
    }

}

public static class Tools
{

    /// <summary>
    /// Convert an array of bytes starting at the offset provided into the specified type (struct or class).
    /// </summary>
    /// <param name="bytes"></param>
    /// <param name="startOffset"></param>
    /// <param name="type"></param>
    /// <returns>Newly created object of the specified type created from bytes given. NULL on any error.</returns>
    public static object ConvertBytesToObject(byte[] bytes, int startOffset, Type type)
    {
        object obj = null;
        int size = Marshal.SizeOf(type);

        if (size > 0)
        {
            if (size <= bytes.Length - startOffset)
            {
                IntPtr ptr = IntPtr.Zero;

                try
                {
                    ptr = Marshal.AllocHGlobal(size);
                    Marshal.Copy(bytes, startOffset, ptr, size);
                    obj = Marshal.PtrToStructure(ptr, type);
                }
                finally
                {
                    if (ptr != IntPtr.Zero)
                    {
                        Marshal.FreeHGlobal(ptr);
                    }
                }
            }
            else
            {
                throw new Exception("ConvertBytesToObject: Requested offset + size of object exceeds length of bytes buffer to read.");
            }
        }
        else
        {
            throw new Exception("ConvertBytesToObject: Marshal.SizeOf(T) returned a size of 0.");
        }

        return obj;
    }

    /// <summary>
    /// Convert an object (struct or class) to an array of bytes.
    /// </summary>
    /// <param name="obj"></param>
    /// <returns></returns>
    public static byte[] ConvertObjectToBytes(object obj)
    {
        byte[] bytes = null;

        if (obj != null)
        {
            IntPtr ptr = IntPtr.Zero;
            Type type = obj.GetType();
            int size = Marshal.SizeOf(type);
            //int size = Marshal.SizeOf(obj.GetType());

            if (size > 0)
            {
                try
                {
                    ptr = Marshal.AllocHGlobal(size);
                    if (ptr != IntPtr.Zero)
                    {
                        Marshal.StructureToPtr(obj, ptr, false);
                        bytes = new byte[size];
                        Marshal.Copy(ptr, bytes, 0, size);
                    }
                }
                finally
                {
                    if (ptr != IntPtr.Zero)
                    {
                        Marshal.FreeHGlobal(ptr);
                    }
                }
            }
            else
            {
                throw new Exception("ConvertObjectToBytes: Marshal.SizeOf(T) returned a size of 0.");
            }
        }

        return bytes;
    }




}

}

最佳答案

如果您在程序集上运行 ildasm.exe,那么问题的原因就会很清楚:

enter image description here

注意神秘<FirmwareRevision>k__BackingField添加到类中的字段。您可能会猜到它来自哪里,它是自动属性 ​​FirmwareRevision 的自动生成的支持字段。 native 代码看到的是字段,而不是属性。这使它们的顺序错误。

修复很简单,您需要显式声明支持字段并避免让编译器生成它:

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public class Complex {
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
    private byte[] _headerInfo = new byte[5];
    private uint firmwareRevision;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 9)]
    private byte[] _softwarePartNumber = new byte[9];

    public uint FirmwareRevision {
        get { return firmwareRevision; }
        set { firmwareRevision = value; }
    }
    //etc..
}

注意 Pack = 1,它通常不正确。在这种情况下这很重要,因为第一个字段的长度是 5 个字节。自然对齐将固件版本放在偏移量 8 处,您将在偏移量 5 处得到它。

关于C# 编码问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7044458/

相关文章:

.net - 在 Visual Studio 中单步执行 "managed to native transition"?

c# - 为什么 Visual Studio 2010 C# Express 不能正确格式化嵌套代码?

c# - WPF 绑定(bind)到子集合

c# - Entity Framework 7 内存数据库异常

c# - 在 C++ 和 C# 之间编码类实例的指针

json - 在 Go 中将嵌套的 JSON 解码为平面结构

c# - C# 编码结构指针之间有什么区别?

c# - 使用 Windows Phone 在运行时加载 xaml viewmodel

c# - 存储库模式逐步解释

C# 调用非托管 C 驱动程序 (dll)