我有这个结构和这段代码:
[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数据
阅读this MSDN page后说
以下复杂类型也是blittable类型:
blittable类型的一维数组,例如整数数组。但是,包含blittable类型的可变数组的类型本身不会是blittable。
仅包含blittable类型的格式化值类型(如果它们被编组为格式化类型,则为类)。有关格式化值类型的详细信息,请参阅值类型的默认封送。
我不明白我做错了什么。
我不仅想要使用Marshal
,还要了解这一点。
所以我真正想知道的是:
我使用的是.Net 4.5,但也需要.Net 2.0的解决方案。
答案 0 :(得分: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];
}
请注意,您必须将声明更改为 struct 类型。它现在是一个值类型,当您将其作为局部变量时,不再需要使用GCHandle来固定该值。确保任何非托管代码通常通过引用获取结构值,不存储指向结构的指针。这将会严重破坏并完全不可抗拒。 unsafe 关键字在此处是合适的。如果它确实存储了指针,那么你真的必须使用Marshal.AllocHGlobal()和Marshal.StructureToPtr()来确保指针在非托管代码使用时保持有效。
答案 1 :(得分:3)
.NET令人讨厌的局限是它识别的唯一数组是独立的System.Array
对象和System.String
,它们都是引用类型。用C#编写的代码可能使用fixed
数组(如Hans Passant所述),但.NET本身无法识别这种类型,并且使用固定数组的代码无法验证。此外,固定数组仅限于保存基元,并且不能被其他语言(如vb.net)访问。
使用固定数组的两种方法是
用一些字段组合替换固定数组,这些字段总共有适当的大小(在大多数情况下使用N个变量,但可能用char[4]
替换UInt32
,或者{ {1}} char[8]
。如果数组不是太大,可以定义(通过剪切/粘贴或反射)一组静态方法,这些方法通过ref接受struct并读/写正确的元素,然后创建一个委托数组来调用这样的方法
用数组替换整个结构,然后将该数组的第一个元素作为UInt64
参数传递。这可能比在结构中使用ref
数组更加“危险”,但这是我在vb.net中知道的唯一方法来获取“pass-by-ref”语义,其结构包含确实需要作为一个数组进行访问。
虽然我可以理解值类型数组可能被认为是“令人困惑”(特别是如果它们是自动装箱的),但是从某些地方来看,它们可能是用于阵列存储的语义正确方法。允许COM互操作的pass-by-ref语义,以及从应该返回少量值的方法的角度出发。例如,在fixed
中,有一种方法可以将当前图形变换作为System.Drawing2d
返回;除了通过实验之外,没有明确的方法可以知道返回后对该数组的更改是否会影响,可能会影响或保证不会影响其他任何内容。如果该方法返回一个值类型数组,则很明显对返回的数组的更改不会影响其他任何内容。尽管如此,无论值类型数组是否会成为框架的有用部分,事实仍然是无论是好的还是坏的原因都不存在这样的事情。
答案 2 :(得分:1)
我从这个链接(here)
中得到了以下答案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);