Is there an alternative for the StructLayout "Pack" attribute in the Compact Framework?

I would like to do the following:

[StructLayout(LayoutKind.Sequential, Pack = 1)] public struct SomeStruct { public byte SomeByte; public int SomeInt; public short SomeShort; public byte SomeByte2; } 

Is there an alternative since the package is not supported in a compact structure?

Update. Explicitly adjusting the structure and providing a FieldOffset for each of them does not work, since this does not affect how the structure is packed.

Update2: if you try the following, the CF program will not even run due to how the structure is packed:

 [StructLayout(LayoutKind.Explicit, Size=8)] public struct SomeStruct { [FieldOffset(0)] public byte SomeByte; [FieldOffset(1)] public int SomeInt; [FieldOffset(5)] public short SomeShort; [FieldOffset(7)] public byte SomeByte2; } 

I know this is hard to believe, but if you try, you will see. Add it to the CF project and try running it and you will get a TypeLoadException. Changing the offsets to 0.4.8.10, respectively, and it will work (but the size ends with 12).

I was hoping that someone might have a solution that uses reflection, perhaps for each field size of each individual type (something includes recursion to handle structures inside structures or type arrays).

+6
c # struct compact-framework
source share
7 answers

This is probably not the type of answer you are looking for, but I will send it to hell anyway:

 public struct SomeStruct { public byte SomeByte; public int SomeInt; public short SomeShort; public byte SomeByte2; public byte[] APIStruct { get { byte[] output = new byte[8]; output[0] = this.SomeByte; Array.Copy(BitConverter.GetBytes(this.SomeInt), 0, output, 1, 4); Array.Copy(BitConverter.GetBytes(this.SomeShort), 0, output, 5, 2); output[7] = this.SomeByte2; return output; } set { byte[] input = value; this.SomeByte = input[0]; this.SomeInt = BitConverter.ToInt32(input, 1); this.SomeShort = BitConverter.ToInt16(input, 5); this.SomeByte2 = input[7]; } } } 

Basically, it packs / unpacks itself in the APIStruct property.

+6
source share

The easiest way to solve this type of problem is with the same lines as for a bit field, just pack your data into a private member (or members, if large) of the corresponding data type, and then imagine public properties that decompress the data for you. Unpacking operations are very fast and have little effect on performance. For your specific type, you probably need the following:

 public struct SomeStruct { private long data; public byte SomeByte { get { return (byte)(data & 0x0FF); } } public int SomeInt { get { return (int)((data & 0xFFFFFFFF00) << 8); } } public short SomeShort { get { return (short)((data & 0xFFFF0000000000) << 40); } } public byte SomeByte2 { get { return (byte)((data & unchecked((long)0xFF00000000000000)) << 56); } } } 

For some structures, even this method does not work due to an unsuccessful way to determine the structure. In such cases, you will usually have to use an array of bytes as a data block from which the elements can be unpacked.

EDIT: Extend what I mean about structures that cannot be processed with this simple method. When you cannot make packing / unpacking simple, you need to manually march an irregular structure. This can be done using manual methods at the moment when you call the pInvoked API or using your own marshaler. The following is an example of a custom marshaler that can be easily adapted to manual on-site marshaling.

 using System.Runtime.InteropServices; using System.Threading; public class Sample { [DllImport("sample.dll")] public static extern void TestDataMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(TestDataMarshaler))] TestDataStruct pData); } public class TestDataStruct { public byte data1; public int data2; public byte[] data3 = new byte[7]; public long data4; public byte data5; } public class TestDataMarshaler : ICustomMarshaler { //thread static since this could be called on //multiple threads at the same time. [ThreadStatic()] private static TestDataStruct m_MarshaledInstance; private static ICustomMarshaler m_Instance = new TestDataMarshaler(); public static ICustomFormatter GetInstance(string cookie) { return m_Instance; } #region ICustomMarshaler Members public void CleanUpManagedData(object ManagedObj) { //nothing to do. } public void CleanUpNativeData(IntPtr pNativeData) { Marshal.FreeHGlobal(pNativeData); } public int GetNativeDataSize() { return 21; } public IntPtr MarshalManagedToNative(object ManagedObj) { m_MarshaledInstance = (TestDataStruct)ManagedObj; IntPtr nativeData = Marshal.AllocHGlobal(GetNativeDataSize()); if (m_MarshaledInstance != null) { unsafe //unsafe is simpler but can easily be done without unsafe if necessary { byte* pData = (byte*)nativeData; *pData = m_MarshaledInstance.data1; *(int*)(pData + 1) = m_MarshaledInstance.data2; Marshal.Copy(m_MarshaledInstance.data3, 0, (IntPtr)(pData + 5), 7); *(long*)(pData + 12) = m_MarshaledInstance.data4; *(pData + 20) = m_MarshaledInstance.data5; } } return nativeData; } public object MarshalNativeToManaged(IntPtr pNativeData) { TestDataStruct data = m_MarshaledInstance; m_MarshaledInstance = null; //clear out TLS for next call. if (data == null) data = new TestDataStruct(); //if no in object then return a new one unsafe //unsafe is simpler but can easily be done without unsafe if necessary { byte* pData = (byte*)pNativeData; data.data1 = *pData; data.data2 = *(int*)(pData + 1); Marshal.Copy((IntPtr)(pData + 5), data.data3, 0, 7); data.data4 = *(long*)(pData + 12); data.data5 = *(pData + 20); } return data; } #endregion } 

In the case of arrays of these structures, you cannot use custom marshaling if the size of the array is not fixed, but it is relatively easy to manually marshal the data of the array as a whole using the same methods.

+4
source share

Do you need an absolutely specific layout or is it acceptable to just make size 8?

I ask about it because it is laid out as follows

 [StructLayout(LayoutKind.Explicit, Size=8)] public struct SomeStruct { [FieldOffset(0)] public byte SomeByte; [FieldOffset(1)] public int SomeInt; [FieldOffset(5)] public short SomeShort; [FieldOffset(7)] public byte SomeByte2; } 

Has non-aligned word fields that may be causing your problem.

If you can reorder things, this may work for you:

 [StructLayout(LayoutKind.Explicit, Size=8)] public struct SomeStruct { [FieldOffset(0)] public byte SomeByte; [FieldOffset(1)] public byte SomeByte2; [FieldOffset(2)] public short SomeShort; [FieldOffset(4)] public int SomeInt; } 

When I test with this on an emulator, it works great.

Obviously, if you are not ready to allow regrouping, you cannot do anything.

This answer and this old article will strongly indicate that you should align your structures to the minimum multiple of their size (I tried with int aligned at offset 2, and this also caused an error)

Given your need to interact with external data, the most likely solution is to:

 [StructLayout(LayoutKind.Explicit, Size=8)] public struct SomeStruct { [FieldOffset(0)] private byte b0; [FieldOffset(1)] private byte b1; [FieldOffset(2)] private byte b2; [FieldOffset(3)] private byte b3; [FieldOffset(4)] private byte b4; [FieldOffset(5)] private byte b5; [FieldOffset(6)] private byte b6; [FieldOffset(7)] private byte b7; // not thread safe - alter accordingly if that is a requirement private readonly static byte[] scratch = new byte[4]; public byte SomeByte { get { return b0; } set { b0 = value; } } public int SomeInt { get { // get the right endianess for your system this is just an example! scratch[0] = b1; scratch[1] = b2; scratch[2] = b3; scratch[3] = b4; return BitConverter.ToInt32(scratch, 0); } } public short SomeShort { get { // get the right endianess for your system this is just an example! scratch[0] = b5; scratch[1] = b6; return BitConverter.ToInt16(scratch, 0); } } public byte SomeByte2 { get { return b7; } set { b7 = value; } } } 
+2
source share

I think we need to accept Steven Martin's answer, get him to accept T and use reflection to universally use the MarshalManagedToNative and MarshalNativeToManaged methods. Then you will have a custom block marker that will work for any type of structure.

Here is the code:

 using System; using System.Threading; using System.Reflection; using System.Runtime.InteropServices; namespace System.Runtime.InteropServices { public class PinnedObject : IDisposable { private GCHandle gcHandle = new GCHandle(); public PinnedObject(object o) { gcHandle = GCHandle.Alloc(o, GCHandleType.Pinned); } public unsafe static implicit operator byte*(PinnedObject po) { return (byte*)po.gcHandle.AddrOfPinnedObject(); } #region IDisposable Members public void Dispose() { if (gcHandle.IsAllocated) { gcHandle.Free(); } } #endregion } public class PackedStructMarshaler<T> : ICustomMarshaler where T : struct { private static ICustomMarshaler m_instance = new PackedStructMarshaler<T>(); public static ICustomMarshaler GetInstance() { return m_instance; } private void ForEachField(Action<FieldInfo> action) { foreach (var fi in typeof(T).GetFields(BindingFlags.Public | BindingFlags.NonPublic)) { // System.Diagnostics.Debug.Assert(fi.IsValueType); action(fi); } } private unsafe void MemCpy(byte* dst, byte* src, int numBytes) { for (int i = 0; i < numBytes; i++) { dst[i] = src[i]; } } #region ICustomMarshaler Members public void CleanUpManagedData(object ManagedObj) { } public void CleanUpNativeData(IntPtr pNativeData) { Marshal.FreeHGlobal(pNativeData); } public int GetNativeDataSize() { unsafe { int ret = 0; ForEachField( (FieldInfo fi) => { Type ft = fi.FieldType; ret += Marshal.SizeOf(ft); }); return ret; } } private object m_marshaledObj = null; public unsafe IntPtr MarshalManagedToNative(object obj) { IntPtr nativeData = (IntPtr)0; if (obj != null) { if (m_marshaledObj != null) throw new ApplicationException("This instance has already marshaled a managed type"); m_marshaledObj = obj; nativeData = Marshal.AllocHGlobal(GetNativeDataSize()); byte* pData = (byte*)nativeData; int offset = 0; ForEachField( (FieldInfo fi) => { int size = Marshal.SizeOf(fi.FieldType); using (PinnedObject po = new PinnedObject(fi.GetValue(obj))) { MemCpy(pData + offset, po, size); } offset += size; }); } return nativeData; } public object MarshalNativeToManaged(IntPtr pNativeData) { if (m_marshaledObj != null) m_marshaledObj = null; unsafe { byte* pData = (byte*)pNativeData; int offset = 0; object res = new T(); ForEachField( (FieldInfo fi) => { int size = Marshal.SizeOf(fi.FieldType); fi.SetValue(res, (object)(*((byte*)(pData + offset)))); offset += size; }); return res; } } #endregion } } 
+1
source share

You need to post a more suitable example. Installing packaging on this structure would have no effect.

My bet is that you need to use LaoutKind.Explicit, and then give biases for each member. In any case, this is better than messing with the packaging, because it is more obvious to someone who is looking at the code that the original developer is clearly designed to make things uneven.

Something like that:

 [StructLayout(LayoutKind.Explicit)] struct Foo { [FieldOffset(0)] byte a; [FieldOffset(1)] uint b; } 
0
source share

LayoutKind.Explicit would be the best choice for defining specific memory layouts. However, do not use LayoutKind.Explicit for structures that contain pointer size values , such as true pointers, operating system descriptors, or IntPtr s; it just requires mysterious run-time problems on random platforms.

In particular, LayoutKind.Explicit is a poor substitute for anonymous unions . If your target structure contains an anonymous union, convert it to a named union; you can safely imagine a named union as a structure with LayoutKind.Explicit , where all offsets are 0 .

0
source share

LayoutKind.Explicit and FieldOffsetAttribute will let you do everything you can with the Pack property. These explicit layout attributes allow you to specify the exact byte position of each field in the structure (relative to the beginning of the range of the memory structure). The Pack property is used by the runtime to determine the exact position of each field when using sequential layout. The pack property has no other effect, so using an explicit layout allows you to emulate the same behavior, albeit in a bit more detail. If you don’t think this solves your problem, perhaps you can post a little more information about what you are trying to do, or why you think you need to use the Pack property.

Edit: I just noticed an additional comment about trying to get the whole structure size up to 8 bytes. Have you tried to use the StructLayoutAttribute.Size property? Unlike Pack, it is available in the Compact Framework.

0
source share

All Articles