【问题标题】:Convert Byte array to struct in VS2013 debugger在 VS2013 调试器中将字节数组转换为结构
【发布时间】:2014-12-01 15:59:29
【问题描述】:

我喜欢在 C# 中使用的一个简单技巧是覆盖 ToString(),以便调试器在监视窗口中显示开发人员指定的、人类可读的字符串。真的很方便。

目前,我正在调试一个 256 字节的数据包,它是 ModBus/TCP 的轻微变体。我不想在监视窗口中查看 256 个数组索引,而是希望看到类似“transaction_id_high”、“transaction_id_low”等的内容,其中映射是 1:1,因为字段是在结构中定义的。

当我尝试将(ModBusPacket)response_buffer 放入监视窗口以查看会发生什么时,它回复为Cannot convert type 'byte[]' to 'ModBusPacket'

有没有人尝试过这样做并成功了?

ModBus 包:

public struct ModBusPacket
{
    char transaction_id_high;
    char transaction_id_low;
    char protocol_id_high;
    char protocol_id_low;
    char unit_id;
    char function_code;
    char sub_unit_id;
    char[] data;
}

字节数组只是

byte[] response_buffer = new byte[256];

【问题讨论】:

  • 查看Marshal类。
  • 如果您显示Struct 的结构以及与您的问题和/或问题相关的所有相关代码,这真的很有帮助。我们看不到你所看到的
  • 如果您的 ModBusPacket 有从数组创建的构造函数/助手 - 您可以使用即时窗口来构造对象 (new ModBusPacket(response_buffer) ) 并查看其属性。
  • 假设您的ModBusPacket 结构可以编组,您可以这样做:stackoverflow.com/questions/2871/…,然后在即时窗口中调用该方法。
  • 您确定要char 来处理所有内容吗,因为char 在c# 中是一个2 字节的unicode 字符?

标签: c# struct visual-studio-2013 type-conversion bytearray


【解决方案1】:

如果您的数据包基于this,我不建议使用char 来表示字节,因为c# 中的char 是一个16 位数字(序数)值。相反,我建议将 byte 用于 8 位无符号值,将 UInt16 用于 16 位无符号值。然后你可以这样做:

[StructLayout(LayoutKind.Sequential)]
public struct ModBusPacket
{
    // http://en.wikipedia.org/wiki/Modbus#Frame_format
    // The byte order is Big-Endian (first byte contains MSB).
    public const bool IsLittleEndian = false;

    public UInt16 TransactionIdentifier;
    public UInt16 ProtocolIdentifier;
    public UInt16 Length;
    public byte UnitIdentifier;
    public byte FunctionCode;
    public byte[] Data;

    static int PostIncrement(ref int index, int inc)
    {
        int old = index;
        index += inc;
        return old;
    }

    static byte[] ElementArray(byte[] buffer, ref byte[] swapBuffer, ref int index, int size)
    {
        if (swapBuffer == null || swapBuffer.Length < size)
            Array.Resize(ref swapBuffer, size);
        Array.Copy(buffer, PostIncrement(ref index, size), swapBuffer, 0, size);
        if (BitConverter.IsLittleEndian != IsLittleEndian)
            Array.Reverse(swapBuffer);
        return swapBuffer;
    }

    public ModBusPacket(byte[] buffer)
    {
        int pos = 0;
        byte[] swapBuffer = null;

        TransactionIdentifier = (buffer.Length >= pos + 2 ? BitConverter.ToUInt16(ElementArray(buffer, ref swapBuffer, ref pos, 2), 0) : (UInt16)0);
        ProtocolIdentifier = (buffer.Length >= pos + 2 ? BitConverter.ToUInt16(ElementArray(buffer, ref swapBuffer, ref pos, 2), 0) : (UInt16)0);
        Length = (buffer.Length >= pos + 2 ? BitConverter.ToUInt16(ElementArray(buffer, ref swapBuffer, ref pos, 2), 0) : (UInt16)0);
        UnitIdentifier = (buffer.Length >= pos + 1 ? buffer[PostIncrement(ref pos, 1)] : (byte)0);
        FunctionCode = (buffer.Length >= pos + 1 ? buffer[PostIncrement(ref pos, 1)] : (byte)0);
        var length = Math.Max(buffer.Length - pos, 0);
        Data = new byte[length];
        if (length > 0)
            Array.Copy(buffer, pos, Data, 0, length);
    }

    public override string ToString()
    {
        return ObjectExtensions.ToStringWithReflection(this);
    }
}

public static class ObjectExtensions
{
    public static string ToStringWithReflection<T>(this T obj)
    {
        if (obj == null)
            return string.Empty;
        var type = obj.GetType();
        var fields = type.GetFields();
        var properties = type.GetProperties().Where(p => p.GetIndexParameters().Length == 0 && p.GetGetMethod(true) != null);

        var values = new List<KeyValuePair<string, object>>();
        Array.ForEach(fields, (field) => values.Add(new KeyValuePair<string, object>(field.Name, field.GetValue(obj))));
        foreach (var property in properties)
            if (property.CanRead)
                values.Add(new KeyValuePair<string, object>(property.Name, property.GetValue(obj, null)));

        return values.Aggregate(new StringBuilder(), (s, pair) => (s.Length == 0 ? s.Append("{").Append(obj.GetType().Name).Append(": ") : s.Append("; ")).Append(pair)).Append("}").ToString();
    }
}

完成后,在即时窗口中,您可以在即时窗口或监视窗口中键入buffer.ToPacket() 并查看格式化数据。或者您可以使用conversion operator 将您的字节数组转换为ModBusPacket,如果这样更有吸引力的话。

【讨论】:

  • 感谢您的出色回答,dbc!你的提议教会了我很多。它几乎可以工作了,我应该能够找出问题所在,这只是 BitConverter 没有正确转换长度。除此之外,这个答案是完美的。再次感谢!
  • 哦,呵呵... BitConverter 的字节序与我的数据相反。其他一切都可能有效,因为它们的值为 0。:)
  • @Dave - 我根据您对字节序的反馈更新了答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-14
  • 1970-01-01
  • 1970-01-01
  • 2011-03-17
  • 1970-01-01
相关资源
最近更新 更多