文号
821140
阅读
6288
回复
4
收藏
3
C#中读写byte[]数组中的结构体
2016/06/08
IP:四川

C++中,可以通过指针强制转换来读取char[]中的结构体,但是到了C#中,这一招行不通了。

通过查找资料发现,在C#中可以有多种读取byte[]中的结构体的方法,但是我感觉最可靠的还是使用BitConverter:

  • 从byte[]读取字段,使用field = BitConverter.ToXXX(bytes, offset)实现。
  • 向byte[]写入字段,使用BitConverter.GetBytes(field).CopyTo(bytes, offset)实现。

注意定长字符串不能用BitConverter.ToString,它是用来返回16进制表示的。对于定长字符串来说:

  • 从byte[]读取定长字符串,可以这样实现:
field = Encoding.XXX.GetString(bytes, offset, length).Split('\0')[0];
  • 向byte[]写入定长字符串就麻烦点了,因为需要控制缓冲区长度:
byte[] data = new byte[length];
Encoding.XXX.GetBytes(field).CopyTo(data, 0);
data.CopyTo(bytes, offset);

示例代码如下:

public enum BICompression : uint
{
    BI_RGB = 0, // 不压缩
    BI_RLE8 = 1, // 8位运行长度编码
    BI_RLE4 = 2, // 4位运行长度编码
    BI_BITFIELDS = 3 // 使用颜色位屏蔽
}

public struct BitmapInfoHeader
{
    public uint biSize; // 结构的大小>=40
    public int biWidth; // 图像宽度(像素)
    public int biHeight; // 图像高度(像素)
    public ushort biPlanes; // = 1
    public ushort biBitCount; // 每像素的位数(1,4,8,16,24,32)
    public BICompression biCompression; // 压缩代码
    public uint biSizeImage; // 图像的字节数
    public int biXPelsPerMeter; // 横向分辨率
    public int biYPelsPerMeter; // 纵向分辨率
    public uint biClrUsed; // 使用的颜色数
    public uint biClrImportant; // 重要颜色数
    public BitmapInfoHeader(byte[] src, int off)
    {
        biSize = BitConverter.ToUInt32(src, off);
        biWidth = BitConverter.ToInt32(src, off + 4);
        biHeight = BitConverter.ToInt32(src, off + 8);
        biPlanes = BitConverter.ToUInt16(src, off + 12);
        biBitCount = BitConverter.ToUInt16(src, off + 14);
        biCompression = (BICompression)BitConverter.ToUInt32(src, off + 16);
        biSizeImage = BitConverter.ToUInt32(src, off + 20);
        biXPelsPerMeter = BitConverter.ToInt32(src, off + 24);
        biYPelsPerMeter = BitConverter.ToInt32(src, off + 28);
        biClrUsed = BitConverter.ToUInt32(src, off + 32);
        biClrImportant = BitConverter.ToUInt32(src, off + 36);
    }
    public void WriteBytes(byte[] dst, int off)
    {
        BitConverter.GetBytes(biSize).CopyTo(dst, off);
        BitConverter.GetBytes(biWidth).CopyTo(dst, off + 4);
        BitConverter.GetBytes(biHeight).CopyTo(dst, off + 8);
        BitConverter.GetBytes(biPlanes).CopyTo(dst, off + 12);
        BitConverter.GetBytes(biBitCount).CopyTo(dst, off + 14);
        BitConverter.GetBytes((uint)biCompression).CopyTo(dst, off + 16);
        BitConverter.GetBytes(biSizeImage).CopyTo(dst, off + 20);
        BitConverter.GetBytes(biXPelsPerMeter).CopyTo(dst, off + 24);
        BitConverter.GetBytes(biYPelsPerMeter).CopyTo(dst, off + 28);
        BitConverter.GetBytes(biClrUsed).CopyTo(dst, off + 32);
        BitConverter.GetBytes(biClrImportant).CopyTo(dst, off + 36);
    }
}
[ 修改于10年6个月前 - 2016/06/09 10:34:28 ]
acmilan作者
10年6个月前修改于 10年6个月前IP:四川
821141
1楼

对于和一般的WinAPI交互来说,没必要这么麻烦,可以使用StructLayout和MarshalAs解决:

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;

namespace ConsoleApplication5
{
    class Program
    {
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        public struct RtlOSVersionInfoExW
        {
            public uint dwOSVersionInfoSize;
            public uint dwMajorVersion;
            public uint dwMinorVersion;
            public uint dwBuildNumber;
            public uint dwPlatformId;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
            public string szCSDVersion;
            public ushort wServicePackMajor;
            public ushort wServicePackMinor;
            public ushort wSuiteMask;
            public byte wProductType;
            public byte wReserved;
        }

        [DllImport("ntdll.dll")]
        public static extern bool RtlGetVersion(ref RtlOSVersionInfoExW osver);

        static void Main(string[] args)
        {
            RtlOSVersionInfoExW osvex = new RtlOSVersionInfoExW();
            osvex.dwOSVersionInfoSize = (uint)Marshal.SizeOf(osvex);
            RtlGetVersion(ref osvex);
            Console.WriteLine(osvex.dwMajorVersion);
            Console.WriteLine(osvex.dwMinorVersion);
            Console.WriteLine(osvex.dwBuildNumber);
            Console.WriteLine(osvex.dwPlatformId);
            Console.WriteLine(osvex.szCSDVersion);
            Console.WriteLine(osvex.wServicePackMajor);
            Console.WriteLine(osvex.wServicePackMinor);
            Console.WriteLine(osvex.wSuiteMask);
            Console.WriteLine(osvex.wProductType);
            Console.WriteLine(osvex.wReserved);
        }
    }
}
引用
评论
10年6个月前IP:江苏
821213
2楼

/// <summary> /// 结构体 值类型转byte数组 /// BitConverter /// </summary> /// <typeparam name="T"></typeparam> /// <param name="structObj"></param> /// <returns></returns> public static byte[] StructToBytes<T>(this T structObj) where T : struct { int size = System.Runtime.InteropServices.Marshal.SizeOf(structObj); byte[] bytes = new byte[size];

        IntPtr structPtr = System.Runtime.InteropServices.Marshal.AllocHGlobal(size);
        System.Runtime.InteropServices.Marshal.StructureToPtr(structObj, structPtr, true);
        System.Runtime.InteropServices.Marshal.Copy(structPtr, bytes, 0, size);
        System.Runtime.InteropServices.Marshal.FreeHGlobal(structPtr);
        return bytes;
    }
    /// <summary>
    /// byte数组转 结构体 值类型
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="bytes">
    /// <returns></returns>
    public static T BytesToStuct<t>(this byte[] bytes) where T : struct
    {
        int size = System.Runtime.InteropServices.Marshal.SizeOf(default(T));
        if (size > bytes.Length)
        {
            return default(T);
        }
        IntPtr structPtr = System.Runtime.InteropServices.Marshal.AllocHGlobal(size);
        System.Runtime.InteropServices.Marshal.Copy(bytes, 0, structPtr, size);
        T obj = (T)System.Runtime.InteropServices.Marshal.PtrToStructure(structPtr, typeof(T));
        System.Runtime.InteropServices.Marshal.FreeHGlobal(structPtr);

        return obj;
    }
</t>

/// <summary> /// 把对象序列化并返回相应的字节 /// </summary> /// <param name="pObj">需要序列化的对象</param> /// <returns>byte[]</returns> static public byte[] SerializeObject(this object pObj) { if (pObj == null) return null; XXXXXXXXX.MemoryStream _memory = new XXXXXXXXX.MemoryStream(); BinaryFormatter formatter = new BinaryFormatter(); formatter.Serialize(_memory, pObj); _memory.Position = 0; byte[] read = new byte[_memory.Length]; _memory.Read(read, 0, read.Length); _memory.Close(); return read; }

    /// <summary>
    /// 把字节反序列化成相应的对象
    /// </summary>
    /// <param name="pBytes">字节流
    /// <returns>object</returns>
    static public T DeserializeObject<t>(this byte[] pBytes)
    {
        T _newOjb ;
        if (pBytes == null || pBytes.Length < 1) return default(T);
        System.IO.MemoryStream _memory = new System.IO.MemoryStream(pBytes);
        _memory.Position = 0;
        BinaryFormatter formatter = new BinaryFormatter();
        _newOjb = (T)formatter.Deserialize(_memory);
        _memory.Close();
        return _newOjb;
    }
</t>
引用
评论
acmilan作者
10年6个月前IP:四川
821250
3楼

引用 张静茹 : /// <summary> /// 结构体 值类型转byte数组 /// BitConverter /// </summary> ///……

写个算法也要用到互操作性,总感觉不爽。。。

引用
评论
10年6个月前修改于 10年6个月前IP:广东
821452
4楼

InteropServices.Marshal Methods are probably the fastest way to manipulate bytes directly from memory in .NET

引用
评论
笔记
{{note.content}}
{{n.user.username}}
{{fromNow(n.toc)}} {{n.status === noteStatus.disabled ? "已屏蔽" : ""}} {{n.status === noteStatus.unknown ? "正在审核" : ""}} {{n.status === noteStatus.deleted ? '已删除' : ''}}
  • 编辑
  • 删除
  • {{n.status === 'disabled' ? "解除屏蔽" : "屏蔽" }}
我也是有底线的