C# “StructLayout”是否有替代方案;“包装”;紧凑框架中的属性?
我想做以下工作:C# “StructLayout”是否有替代方案;“包装”;紧凑框架中的属性?,c#,struct,compact-framework,structlayout,C#,Struct,Compact Framework,Structlayout,我想做以下工作: [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct SomeStruct { public byte SomeByte; public int SomeInt; public short SomeShort; public byte SomeByte2; } 由于compact框架不支持Pack,是否有替代方案 更新:显式设置结构并为每个结构
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct SomeStruct
{
public byte SomeByte;
public int SomeInt;
public short SomeShort;
public byte SomeByte2;
}
由于compact框架不支持Pack,是否有替代方案
更新:显式设置结构并为每个结构指定FieldOffset也不起作用,因为它不会影响结构的打包方式
Update2:如果您尝试以下操作,CF程序甚至不会运行,因为结构是如何打包的:
[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;
}
我知道这似乎很难相信,但如果你尝试一下,你就会明白。将它添加到CF项目并尝试运行它,您将获得TypeLoadException。将偏移量分别更改为0,4,8,10,它将起作用(但大小最终为12)
我希望有人能找到一个解决方案,使用反射来分别封送每个字段类型的大小(涉及递归来处理结构或类型数组中的结构)。您需要发布一个更相关的示例。在该结构上设置打包无论如何都没有效果 我打赌你需要使用LaoutKind.Explicit,然后给出每个成员的偏移量。无论如何,这总比搞乱打包要好得多,因为对于那些查看代码的人来说,这更明显地表明,原始开发人员明确表示要使事情不对齐 大致如下:
[StructLayout(LayoutKind.Explicit)]
struct Foo
{
[FieldOffset(0)]
byte a;
[FieldOffset(1)]
uint b;
}
LayoutKind.Explicit
将是定义特定内存布局的最佳选择。但是,不要对包含指针大小值的结构使用LayoutKind.Explicit
,如真指针、操作系统句柄或IntPtr
s;这只是在随机平台上运行时问一些神秘的问题
尤其是,
LayoutKind.Explicit
是匿名联合的一个糟糕替代品。如果目标结构包含匿名联合,请将其转换为命名联合;您可以使用LayoutKind.Explicit
将命名的联合安全地表示为一个结构,其中所有偏移量都是0
处理此类问题的最简单方法与处理位字段的方法相同,只需将数据打包到一个私有成员(或成员,如果它很大的话)然后显示为您解包数据的公共属性。拆包操作非常快,对性能几乎没有影响。对于您的特定类型,以下可能是您想要的:
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); } }
}
对于这些结构的数组,除非数组大小固定,否则不能使用自定义封送处理,但使用相同的技术手动封送整个数组数据相对容易。LayoutKind.Explicit和FieldOffsetAttribute将允许您对Pack属性执行任何操作。这些显式布局属性允许您指定结构中每个字段的确切字节位置(相对于结构内存范围的开头)。当使用顺序布局时,运行时使用Pack属性帮助确定每个字段的确切位置。pack属性没有其他影响,因此使用显式布局可以让您模拟完全相同的行为,尽管要详细一点。如果您认为这不能解决您的问题,也许您可以发布更多关于您正在尝试做什么或为什么您认为您需要使用Pack属性的信息
编辑:我刚刚注意到关于尝试将整个结构的大小设置为8字节的附加注释。您是否尝试过使用StructLayoutAttribute.Size属性?与Pack不同,它是在紧凑的框架中提供的。这可能不是您想要的答案类型,但无论如何我都会发布它:
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];
}
}
}
基本上,它会在APIStruct属性中自行打包/解包。您是否绝对需要特定的布局,还是可以简单地将尺寸设为8
我问这个问题是因为它的布局如下
[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;
}
具有非单词对齐字段,这可能是导致问题的原因
如果你能“重新安排”事情,那么这可能对你有用:
[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;
}
当我在模拟器上使用它进行测试时,它工作得很好
显然,除非你愿意重新安排,否则你无能为力
并强烈指出,您必须至少在结构大小的倍数上对齐结构(我尝试在偏移量2上使用int对齐,这也触发了错误)
鉴于您需要与外部定义的数据进行互操作,以下可能是最简单的解决方案:
[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; }
}
}
我认为应该采纳Stephen Martin的答案,让它接受T,并使用反射来通用地实现MarshallManagedToNATIVE和MarshallNativeToManaged方法。然后,您将有一个自定义的打包结构封送拆收器,它将适用于任何类型的结构 代码如下:
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
}
}
使用系统;
使用系统线程;
运用系统反思;
使用System.Runtime.InteropServices;
命名空间System.Runtime.InteropServices
{
公共类固定对象:IDisposable
{
私有GCHandle GCHandle=新GCHandle();
公共固定对象(对象o)
{
gcHandle=gcHandle.Alloc(o,GCHandleType.pinted);
}
公共不安全静态隐式运算符字节*(PinnedObject po)
{
返回(字节*)po.gcHandle.AddrOfPinnedObject();
}
#区域IDisposable成员
公共空间处置()
{
if(gcHandle.IsAllocated)
{
gcHandle.Free();
}
}
#端区
}
公共类PackedStructMarshaler:ICustomMarshaler,其中T:struct
{
私有静态ICustomMarshaler m_实例=新的packedStructureMarshaler();
公共静态ICustomMarshaler GetInstance()
{
返回m_实例;
}
私有void ForEachField(操作)
{
foreach(v
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
}
}