为数据创建托管结构

时间:2016-05-16 14:22:20

标签: c# data-structures struct marshalling

昨晚我正在努力清理一些代码,我发现这是我以前玩过的旧游戏的一个端口。我用来清理数据的一个技巧是摆脱为结构制作的home-brew DataOffsetAttribute,然后让它成为一个简单的jane结构然后我可以(稍后)将其转换为更有用。有点像一个人会使用DataLayer。这对于文件中的固定大小数据非常有用。使用这种方法,我即将在两种“数据类型”之间进行转换。

public static byte[] ToByteArray<T>(this T dataStructure) where T : struct
{
    int size = Marshal.SizeOf(dataStructure);
    byte[] arr = new byte[size];

    IntPtr ptr = Marshal.AllocHGlobal(size);
    Marshal.StructureToPtr(dataStructure, ptr, true);
    Marshal.Copy(ptr, arr, 0, size);
    Marshal.FreeHGlobal(ptr);
    return arr;
}

public static T MarshalAs<T>(this byte[] rawDataStructure) where T : struct
{
    var type = typeof(T);
    int size = Marshal.SizeOf(type);
    IntPtr ptr = Marshal.AllocHGlobal(size);
    Marshal.Copy(rawDataStructure, 0, ptr, size);
    T structure = (T)Marshal.PtrToStructure(ptr, type);
    Marshal.FreeHGlobal(ptr);

    return structure;
}

然后我开始怀疑这是否适用于我很久以前在数据变量上工作的另一个项目。这是我希望我的数据结构看起来像

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct RfidReaderResponse
{
    public byte PreambleA;
    public byte PreambleB;
    public byte Length;
    public byte LengthLRC;
    public ushort ReaderId;
    public byte CommandId;
    public byte ErrorCode;
    public byte[] Data;
    public byte LRC;
}

我可能会将前导字节组合成一个ushort并检查它是否是一个特定的值...但这是一个不同的讨论。我有3个已知的好反应,我在当天用于测试。我将这些放入linqpad以及我充满希望的数据结构中。所以这就是我目前用于测试的内容

void Main()
{
    var readerResponses = new string[] 
    {
        //data is null because of error EC
        "AA-BB-05-05-02-39-0C-EC-21", 
        //data is 44-00
        "AA-BB-07-07-02-39-0C-00-44-00-8B", 
         //data is 44-00-20-04-13-5E-1A-A4-33-80
        "AA-BB-0F-0F-02-39-10-00-44-00-20-04-13-5E-1A-A4-33-80-FB",
    };
    readerResponses
        .Select(x=> x.ToByteArray().MarshalAs<RfidReaderResponse>())
        .Dump();
}

现在,如果我注释掉结构的最后两个字段,我会得到我对第一部分响应的期望,但我只是迷失了如何取回数据部分。我更喜欢拥有它,因为我拥有它具有Marshaler理解的一些神奇属性,但到目前为止我无法理解它。我试过了

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct RfidReaderResponse
{
    public byte PreambleA;
    public byte PreambleB;
    public byte Length;
    public byte LengthLRC;
    [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] 
    public IntPtr Data;
}

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct RfidReaderResponse
{
    public byte PreambleA;
    public byte PreambleB;
    public byte Length;
    public byte LengthLRC;
    public ushort ReaderId;
    public byte CommandId;
    public byte ErrorCode;
    [MarshalAs(UnmanagedType.SafeArray, SafeArraySubType=VarEnum.VT_I1)]
    public byte[] Data;
    public byte LRC;
}
经过几个小时的研究后。这两个都没有用,说我的方法无法为我的结构制作一个大小。所以我不知道。我做错了什么?

修改

忘记了将十六进制字符串转换为字节数组的方法

public static byte[] ToByteArray(this string hexString)
{
    hexString = System.Text.RegularExpressions.Regex.Replace(hexString, "[^0-9A-F.]", "").ToUpper();
    if (hexString.Length % 2 == 1)
        throw new Exception("The binary key cannot have an odd number of digits");

    byte[] arr = new byte[hexString.Length >> 1];

    for (int i = 0; i < hexString.Length >> 1; ++i)
    {
        arr[i] = (byte)((GetHexVal(hexString[i << 1]) << 4) + (GetHexVal(hexString[(i << 1) + 1])));
    }

    return arr;
}

private static int GetHexVal(char hex)
{
    int val = (int)hex;
    return val - (val < 58 ? 48 : 55);
}

0 个答案:

没有答案