blittable类型的非blittable错误

Ran*_*agg 12 .net c# struct garbage-collection marshalling

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

[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);

    // ...
}
Run Code Online (Sandbox Code Playgroud)

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

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

看完这个MSDN页面后

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

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

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

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

所以我真正想知道的是:

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

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

Han*_*ant 14

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

这是你得到的异常信息.你正专注于信息的"不可眩晕"部分,但这不是问题所在.这是问题的"非原始"部分.数组是非原始数据类型.

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

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

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

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

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