StructLayout“Pack”是否有替代方案? Compact Framework 中的属性?
我想执行以下操作:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct SomeStruct
{
public byte SomeByte;
public int SomeInt;
public short SomeShort;
public byte SomeByte2;
}
由于紧凑框架不支持 Pack,是否有替代方案?
更新:显式设置结构并为每个结构提供 FieldOffset 也不起作用,因为它不会影响结构的打包方式
更新 2:如果您尝试以下操作,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)。
我希望也许有人有一个使用反射的解决方案,可以单独编组每个字段类型的大小(涉及递归来处理结构或类型数组中的结构)。
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 Pack is not supported in the compact framework?
Update: Explicitly setting up the structure and giving FieldOffset for each does not work either as it does not affect how the struct is packed
Update2: If you try the following, the CF program wont even run because of 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 it seems hard to believe, but if you try it you will see. Add it to a CF project and try to run it and you will get a TypeLoadException. Changing the offsets to 0,4,8,10 respectively and it will work (but the size ends up being 12).
I was hoping maybe someone had a solution using reflection maybe to marshal the size of each of the field types individually (something involving recursion to handle structs within structs or arrays of types).
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
您是否绝对需要特定的布局,或者简单地将尺寸设为 8 是否可以接受?
我问这个是因为布局如下
有非单词对齐字段,这可能是导致您的问题的原因。
如果您可以“重新安排”事物,那么这可能对您有用:
当我在模拟器上测试它时,它工作正常。
显然,除非您愿意允许重新安排,否则您无能为力。
此答案和这篇旧文章强烈表明您必须至少将结构对齐其大小的倍数(我尝试使用int 在偏移量 2 上对齐,这也触发了错误)
鉴于您需要与外部定义的数据进行互操作,那么以下可能是您最简单的解决方案:
Do you absolutely require that specific layout or is it acceptable to simply make the size 8?
I ask this because the lay out as follows
Has non word aligned fields which may be what is causing your problem.
If you can 'rearrange' things then this might work for you:
When I test with this on the emulator it works fine.
Obviously unless you are willing to allow the rearrangement there's nothing you can do.
This answer and this old article would strongly indicate that you must at a minimum align your structs on multiples of their size (I tried with an int aligned on offset 2 and this also triggered the error)
Given your need to interoperate with externally defined data then the following is likely your easiest solution:
您需要发布更相关的示例。 无论如何,在该结构上设置打包不会有任何影响。
我敢打赌,您需要使用 LaoutKind.Explicit,然后给出每个成员的偏移量。 无论如何,这比搞乱包装要好得多,因为对于查看代码的人来说,原始开发人员明确表示要使事情不对齐的人更明显。
沿着这些思路:
You need to post a more relevant example. Setting packing on that struct would have no effect anyway.
My bet is that you need to use LaoutKind.Explicit and then give the offsets for each member. It's way better than messing with the packing anyway, because it's way more obvious to someone looking at the code that the original developer explicitly meant for things to be unaligned.
Something along these lines:
我认为应该采用 Stephen Martin 的答案,使其接受 T,并使用反射来一般实现 MarshalManagedToNative 和 MarshalNativeToManaged 方法。 然后,您将拥有一个适用于任何类型结构的自定义打包结构封送拆收器。
这是代码:
I think one should take Stephen Martin's answer, make it accept a T, and use reflection to generically implement the MarshalManagedToNative and MarshalNativeToManaged methods. Then, you'll have a custom packed struct marshaler that will work for any type of struct.
Here's the code:
LayoutKind.Explicit
将是定义特定内存布局的最佳选择。 但是,不要将LayoutKind.Explicit
用于包含指针大小值的结构,例如真正的指针、操作系统句柄或IntPtr
; 这只是在随机平台上运行时遇到神秘的麻烦。特别是,
LayoutKind.Explicit
是匿名联合的糟糕替代品。 如果您的目标结构包含匿名联合,请将其转换为命名联合; 您可以安全地将命名联合表示为具有LayoutKind.Explicit
的结构,其中所有偏移量均为0
。LayoutKind.Explicit
would be your best bet for defining a specific memory layout. However, do not useLayoutKind.Explicit
for structures that contain pointer-sized values such as true pointers, operating system handles orIntPtr
s; this is just asking for mysterious trouble at runtime 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 represent a named union as a struct withLayoutKind.Explicit
where all offsets are0
.LayoutKind.Explicit 和 FieldOffsetAttribute 将允许您执行任何可以使用 Pack 属性执行的操作。 这些显式布局属性允许您指定结构中每个字段的确切字节位置(相对于结构内存范围的开头)。 运行时使用 Pack 属性来帮助确定使用顺序布局时每个字段的确切位置。 pack 属性没有其他效果,因此使用显式布局可以让您模拟完全相同的行为,尽管更冗长一些。 如果您认为这不能解决您的问题,也许您可以发布更多有关您正在尝试执行的操作或为什么您认为需要使用 Pack 属性的信息。
编辑:我刚刚注意到关于尝试将整个结构的大小变为 8 字节的附加评论。 您是否尝试过使用 StructLayoutAttribute.Size 属性? 与 Pack 不同,它在 Compact Framework 中可用。
LayoutKind.Explicit and FieldOffsetAttribute will allow you to do anything you could do with the Pack property. These explicit layout attributes allow you to specify the exact byte position of each field in the struct (relative to the beginning of the struct's range of memory). The Pack property is used by the runtime to help determine the exact position of each field when using a sequential layout. The pack property has no other effect, so using explicit layout allows you to emulate the exact same behavior, albeit a bit more verbosely. If you don't think this solves your problem, perhaps you could post a bit more information about what you're trying to do or why you think you need to use the Pack property.
Edit: I just noticed the additional comment about trying to get the entire structure's size to 8 bytes. Have you tried using the StructLayoutAttribute.Size property? Unlike Pack, it is available in the Compact Framework.
这可能不是您正在寻找的答案类型,但无论如何我都会将其发布:
基本上它会在 APIStruct 属性中自行打包/解包。
This probably isn't the type of answer you're looking for, but I'll post it anyway for the hell of it:
Basically it does the packing/unpacking itself in the APIStruct property.
处理此类问题的最简单方法与处理位字段的方法相同,只需将数据打包到适当数据类型的私有成员(或成员,如果它很大)中,然后提供公共属性为您解压数据。 解包操作非常快,对性能影响很小。 对于您的特定类型,以下可能是您想要的:
对于某些结构,由于定义结构的方式不幸,甚至此方法也不可行。 在这些情况下,您通常必须使用字节数组作为可以从中解压缩元素的数据块。
编辑:扩展我对无法使用这种简单方法处理的结构的含义。 当您无法像这样进行简单的打包/解包时,您需要手动封送不规则的 struct 。 这可以在调用 pInvoked API 时使用手动方法或使用自定义封送拆收器来完成。 以下是自定义编组器的示例,可以轻松地适应现场手动编组。
对于这些结构的数组,除非数组大小是固定的,否则无法使用自定义封送处理,但使用相同的技术将数组数据手动封送为一个整体相对容易。
The simplest method of dealing with this type of problem is along the same lines as you might for a bit field, simply pack your data into a private member (or members if it is large) of the appropriate data type and then present public properties that unpack the data for you. The unpacking operations are extremely fast and will have little impact on performance. For your particular type the following is probably what you want:
For some structures even this method is not workable due to the unfortunate way a structure has been defined. In those cases you will generally have to use a byte array as a blob of data from which the elements can be unpacked.
EDIT: To expand on what I mean about structs that can't be handled using this simple method. When you can't do simple packing/unpacking like this you need to manually marshal the irregular struct . This can be done using manual methods at the point you call the pInvoked API or by using a custom marshaler. The following is an example of a custom marhsaler that can be easily adapted to on the spot manual marshaling.
In the case of arrays of these structs you can't use custom marshaling unless the array size is fixed but it is relatively easy to manually marshal the array data as a whole using the same techniques.