我想做以下几件事:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct SomeStruct
{
public byte SomeByte;
public int SomeInt;
public short SomeShort;
public byte SomeByte2;
}
既然紧凑框架中不支持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)。
我希望也许有人有一个使用反射的解决方案,也许可以分别封送每个字段类型的大小(涉及到递归来处理结构或类型数组中的结构)。
发布于 2009-08-27 06:12:53
这可能不是你想要的答案,但无论如何我都会把它贴出来:
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属性中自己进行打包/解包。
发布于 2009-08-25 05:09:44
处理这类问题的最简单方法与处理位字段的方法相同,只需将您的数据打包到适当数据类型的私有成员(如果是大型成员)中,然后呈现为您解包数据的公共属性。解包操作速度极快,对性能的影响很小。对于您的特定类型,以下内容可能是您想要的:
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); } }
}
对于一些结构,甚至这种方法也不可行,因为结构的定义方式令人遗憾。在这些情况下,通常必须使用字节数组作为数据的blob,元素可以从中解包。
EDIT:对我所说的不能使用这个简单方法处理的结构进行扩展。当你不能像这样做简单的打包/解包时,你需要手动封送不规则的结构。这可以在调用pInvoked应用编程接口时使用手动方法或使用自定义封送拆收器来完成。下面是一个自定义封送程序的示例,可以很容易地适应现场手动封送处理。
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
}
对于包含这些结构的数组,除非数组大小固定,否则不能使用自定义封送处理,但是使用相同的技术将数组数据作为一个整体进行手动封送处理相对容易。
发布于 2009-08-27 07:11:43
您是否绝对需要特定的布局,或者可以简单地将大小定为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;
}
当我在模拟器上测试它时,它工作得很好。
显然,除非您愿意允许重新排列,否则您无能为力。
This answer和this old article会强烈指示您必须至少将结构按其大小的倍数对齐(我尝试使用偏移量为2的整数对齐,但这也触发了错误)
考虑到您需要与外部定义的数据进行互操作,那么以下可能是您最简单的解决方案:
[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; }
}
}
https://stackoverflow.com/questions/1127601
复制