C#中读写byte[]数组中的结构体
acmilan2016/06/08软件综合 IP:四川

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

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

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

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

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

示例代码如下:

<code class="language-cs">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);
    }
}
</code>

[修改于 8年7个月前 - 2016/06/09 10:34:28]

来自:计算机科学 / 软件综合
4
已屏蔽 原因:{{ notice.reason }}已屏蔽
{{notice.noticeContent}}
~~空空如也
acmilan 作者
8年7个月前 修改于 8年7个月前 IP:四川
821141

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

<code class="language-CS">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);
        }
    }
}
</code>
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
张静茹
8年7个月前 IP:江苏
821213

/// <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 = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXzeOf(structObj); byte[] bytes = new byte[size];

<code>        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></code>

/// <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(); XXXXXXXXXXXXrialize(_memory, pObj); _memory.Position = 0; byte[] read = new byte[_memory.Length]; _XXXXXXXXXad(read, 0, read.Length); _XXXXXXXXXose(); return read; }

<code>    /// <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></code>
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
acmilan作者
8年7个月前 IP:四川
821250

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

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

引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
novakon
8年6个月前 修改于 8年6个月前 IP:广东
821452

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

引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论

想参与大家的讨论?现在就 登录 或者 注册

所属专业
所属分类
上级专业
同级专业
acmilan
进士 学者 笔友
文章
461
回复
2934
学术分
4
2009/05/30注册,5年10个月前活动
暂无简介
主体类型:个人
所属领域:无
认证方式:邮箱
IP归属地:未同步
文件下载
加载中...
{{errorInfo}}
{{downloadWarning}}
你在 {{downloadTime}} 下载过当前文件。
文件名称:{{resource.defaultFile.name}}
下载次数:{{resource.hits}}
上传用户:{{uploader.username}}
所需积分:{{costScores}},{{holdScores}}下载当前附件免费{{description}}
积分不足,去充值
文件已丢失

当前账号的附件下载数量限制如下:
时段 个数
{{f.startingTime}}点 - {{f.endTime}}点 {{f.fileCount}}
视频暂不能访问,请登录试试
仅供内部学术交流或培训使用,请先保存到本地。本内容不代表科创观点,未经原作者同意,请勿转载。
音频暂不能访问,请登录试试
支持的图片格式:jpg, jpeg, png
插入公式
评论控制
加载中...
文号:{{pid}}
投诉或举报
加载中...
{{tip}}
请选择违规类型:
{{reason.type}}

空空如也

加载中...
详情
详情
推送到专栏从专栏移除
设为匿名取消匿名
查看作者
回复
只看作者
加入收藏取消收藏
收藏
取消收藏
折叠回复
置顶取消置顶
评学术分
鼓励
设为精选取消精选
管理提醒
编辑
通过审核
评论控制
退修或删除
历史版本
违规记录
投诉或举报
加入黑名单移除黑名单
查看IP
{{format('YYYY/MM/DD HH:mm:ss', toc)}}