Below is a program that shows a test for marshaling data from a byte
array to a class structure. Unfortunately, there are two annoying
problems (bugs?) that I can't seem to get around.
The first is that if I set the Articulation array to an offset of 22
in the EntityState class instead of 24, I get the following error.
The thing is, it is properly aligned. And the amount of data to be
copied is smaller than the index of 22 anyway.
System.TypeLoadException: Could not load type 'Test.EntityState' from
assembly 'MUSE_Common, Version=7.4.0.25116, Culture=neutral,
PublicKeyToken=null' because it contains an object field at offset 22
that is incorrectly aligned or overlapped by a non-object field.
The next error is that when I set the FieldOffset to 24 for the
articulations array, it runs great ... except that only the first byte
within the "other" array in DeadReckoning is set, they rest of the
bytes are 0 in the array. If I comment out the articulations array,
all the bytes of the "other" array in DeadReckoning are copied
correctly.
Help?
-------------------------------------------------
using System;
using System.Runtime.InteropServices;
namespace MarshalTest
{
[StructLayout(LayoutKind.Explicit, Size = 16)]
public struct Articulation
{
[FieldOffset(0)]
public byte parameterTypeDesignator;
[FieldOffset(1)]
public byte change;
[FieldOffset(2)]
public ushort idAttachedTo;
[FieldOffset(4)]
public uint parameterType;
[FieldOffset(8)]
public uint parameterValue1;
[FieldOffset(12)]
public uint parameterValue2;
}
[StructLayout(LayoutKind.Explicit, Size = 20)]
unsafe public struct DeadReckoning
{
[FieldOffset(0)]
public byte header;
[FieldOffset(1)]
public fixed byte other[15];
[FieldOffset(16)]
public uint footer;
}
[StructLayout(LayoutKind.Explicit)]
public class EntityState
{
[FieldOffset(0)]
public byte mySize;
[FieldOffset(1)]
public DeadReckoning deadRecking;
[FieldOffset(21)]
public byte temp1;
// BUG: Cannot set this offset to 22 without a runtime error.
Why?
[FieldOffset(24)]
[MarshalAs(UnmanagedType.ByValArray, ArraySubType =
UnmanagedType.Struct)]
public Articulation[] articulations;
public EntityState(byte[] rawData)
{
int thisSize = rawData.Length;
IntPtr pData = Marshal.AllocHGlobal(thisSize);
Marshal.Copy(rawData, 0, pData, thisSize);
Marshal.PtrToStructure(pData, this);
Marshal.FreeHGlobal(pData);
mySize = (byte)thisSize;
}
public byte[] ToRaw()
{
byte[] byteArray = new byte[mySize];
IntPtr pointer = Marshal.AllocHGlobal(mySize);
Marshal.StructureToPtr(this, pointer, false);
Marshal.Copy(pointer, byteArray, 0, mySize);
Marshal.FreeHGlobal(pointer);
return byteArray;
}
}
class Program
{
static void Main(string[] args)
{
// Simulate raw data read from hardware
int dataSize = 22;
byte[] rawData = new byte[dataSize];
rawData[0] = (byte)dataSize;
for (int i = 0; i < dataSize; i++)
{
rawData[i] = (byte)(i + 1);
}
// Convert raw data to class
EntityState entity = new EntityState(rawData);
// Compare original data with data stored in class
byte[] rawData2 = entity.ToRaw();
if (rawData.Length != rawData2.Length)
{
Console.WriteLine("**** ERROR *****: Data not the same
length.");
return;
}
bool dataIdentical = true;
// BUG: Offsets 3 - 16 are not identical
for (int i = 1; i < rawData.Length; i++)
{
if (rawData[i] != rawData2[i])
{
Console.WriteLine(i + ":\t" + rawData[i] +
" != " + rawData2[i] + "
*****");
dataIdentical = false;
}
else
{
Console.WriteLine(i + ":\t" + rawData[i] + " == "
+ rawData2[i]);
}
}
if (!dataIdentical)
{
Console.WriteLine("**** ERROR *****: Data not the
same.");
}
}
}
}