blittable类型的非blittable错误

我有这个结构和这段代码:

[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

对象包含非原始或非blittable数据

看完这个MSDN页面后说

以下复杂类型也是blittable类型:

  • blittable类型的一维数组,例如整数数组。 但是,包含blittable类型的可变数组的类型本身不是blittable。

  • 仅包含blittable类型的格式化值类型(如果它们被编组为格式化类型,则为类)。 有关格式化值类型的详细信息,请参阅值类型的默认封送处理。

我不明白我做错了什么。 我不仅想要使用Marshal ,还要了解这一点。

所以我真正想知道的是:

  1. 为什么?
  2. 我该如何解决这个问题?
  3. 您提供的解决方案是否也可以使用结构中的注释行?

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

对象包含非原始或非blittable数据

这是你得到的exception信息。 你关注的是消息中的“不可忽略”部分,但这不是问题所在。 这是问题的“非原始”部分。 数组是非原始数据类型。

CLR正试图让你摆脱困境。 你可以固定对象但是你仍然有问题,arrays将不会固定。 当一个对象具有需要固定的字段时,它不会被真正固定。

而且你对UnmanagedType.ByValArray有一个更大的问题,那就是需要进行结构转换。 换句话说,您需要的布局与托管类对象的布局完全不同。 只有pinvoke marshaller可以进行此转换。

通过使用固定大小的缓冲区,使用fixed关键字,您可以在不使用pinvoke marshaller的情况下获得所需内容。 这需要使用unsafe关键字。 看起来像这样:

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

请注意,您必须将声明更改为结构类型。 它现在是一个值类型,当您将其作为局部变量时,不再需要使用GCHandle来固定该值。 确保任何非托管代码通常通过引用获取结构值, 不会存储指向结构的指针。 这将会严重爆炸,完全不可抗拒。 不安全的关键字在这里是合适的。 如果它确实存储了指针,那么你真的必须对项目符号进行字节化并使用Marshal.AllocHGlobal()和Marshal.StructureToPtr()来确保指针在非托管代码使用时保持有效。

.NET的一个恼人的限制是它识别的唯一数组是一个独立的System.Array对象和一个System.String ,它们都是引用类型。 用C#编写的代码可能使用fixed数组(如Hans Passant所述),但.NET本身无法识别这种类型,使用固定数组的代码无法validation。 此外,固定数组仅限于保存基元,并且不能被其他语言(如vb.net)访问。

使用固定arrays的两种选择是

  • 用一些字段组合替换固定数组,这些字段总共合适大小(在大多数情况下使用N个变量,但可能用UInt32替换例如char[4] ,或用UInt64替换char[8] )。 如果数组不是太大,可以定义(通过剪切/粘贴或reflection)一组静态方法,这些方法通过ref接受结构并读取/写入适当的元素,然后创建一个委托数组来调用这些方法。

  • 用数组替换整个结构,然后将该数组的第一个元素作为ref参数传递。 这可能比在结构中使用fixed数组更加“危险”,但是我在vb.net中知道的唯一方法是获得“pass-by-ref”语义,其结构包含真正需要的东西。作为数组访问。

虽然我可以理解值类型数组可能被认为是“混乱”(特别是如果它们是自动装箱的),但是从允许传递的角度来看,有些地方它们可能是用于arrays存储的语义正确的方法。 COM互操作的by-ref语义,以及应该返回少量值的方法的观点。 例如,在System.Drawing2d ,有一个方法将当前图形变换作为float[6] ; 除了通过实验之外,没有明确的方法可以知道返回后对该数组的更改是否会影响,可能会影响或保证不会影响其他任何内容。 如果该方法返回一个值类型数组,则很明显对返回数组的更改不会影响其他任何内容。 尽管如此,无论值类型数组是否会成为框架的有用部分,事实仍然是无论是出于好的还是坏的原因,都不存在这样的事情。

我从这个链接( 这里 )得到了以下答案

 SItuLongEmailMsg msg = newSItuLongEmailMsg(); // set members msg.text = new byte[2048]; // assign to msg.text int msgSize = Marshal.SizeOf(msg); IntPtr ptr = Marshal.AllocHGlobal(msgSize); Marshal.StructureToPtr(msg, ptr, true); byte[] dataOut = new byte[msgSize]; Marshal.Copy(ptr, dataOut, 0, msgSize);