C# · 12月 23, 2021

c# – 非易失性类型的错误

我有这个结构和这个代码: [StructLayout(LayoutKind.Sequential,Pack = 8)]private class xvid_image_t{ [MarshalAs(UnmanagedType.ByValArray,SizeConst = 4)] public int[] stride; // [MarshalAs(UnmanagedType.ByValArray,SizeConst = 4)] // public IntPtr[] plane;}public int decore(){ xvid_image_t myStruct = new xvid_image_t(); myStruct.stride = new int[4]; // can be commented out – same result GCHandle.Alloc(myStruct,GCHandleType.Pinned); // …}

当我尝试运行它,我得到一个ArgumentException说:

Object contains non-primitive or non-blittable data

阅读this MSDN page后说

The following complex types are also blittable types:

One-dimensional arrays of blittable types,such as an array of integers. However,a type that contains a variable array of blittable types is not itself blittable.

Formatted value types that contain only blittable types (and classes if they are marshaled as formatted types). For more information about formatted value types,see Default Marshaling for Value Types.

我不明白我在做错什么
我不只是想使用元帅,而是要理解这一点.

所以我真正想要的是知道:

>为什么?
>我该如何解决?
>您提供的解决方案是否也可以与结构中的注释行一起使用?

我正在使用.Net 4.5,但也需要.Net 2.0的解决方案.

解决方法

Object contains non-primitive or non-blittable data

这是你得到的例外信息.您正在关注消息的“不可消耗”部分,但这不是问题.这是问题的“非原始”部分.数组是非原始数据类型.

CLR正试图让你在这里摆脱困境.你可以固定对象,但是你仍然有一个问题,数组不会被固定.当一个对象在需要被固定的字段时并没有被真正固定.

而且,与UnmanagedType.ByValArray有更大的问题,需要进行结构转换.换句话说,您所需的布局与被管理类对象的布局完全不同.只有pinvoke编组可以进行这种转换.

使用fixed关键字,您可以使用固定大小的缓冲区,而不使用pinvoke marshaller来获得所需要的内容.这需要使用不安全的关键字.看起来像这样:

[StructLayout(LayoutKind.Sequential)] unsafe private struct xvid_image_t { public fixed int stride[4]; }

请注意,您必须将声明更改为结构类型.它现在是一个值类型,当您将其设置为局部变量时,您不再需要使用GCHandle来锁定该值.确保任何非托管代码通常通过引用获取结构值,不会存储指向结构体的指针.这将会严重恶化,完全不可忽视.不安全的关键字在这里是适当的.如果它存储指针,那么你真的必须使用字节字符串,并使用Marshal.AllocHGlobal()和Marshal.StructureToPtr()来确保指针在非托管代码正在使用时保持有效.