C#中的联合 – 与非对象字段错误对齐或重叠
我正在通过PInvoke编组到本地C dll,它需要以下调用。
private static extern int externalMethod(IntPtr Data, [MarshalAs(UnmanagedType.U4)] ref int dataLength);
dataLength参数是通过IntPtr Data参数传递的struct的长度。 如果两者不匹配,则抛出exception。 外部方法使用C Union连接四种类型。
我已经设法使用FieldOffsetAttribute在C#中重新创建了联合。 然后我计算C#union的长度并使用以下方法调用该方法:
int len = Marshal.SizeOf(data); IntPtr ptr = Marshal.AllocCoTaskMem(len); externalMethod(ptr, len);
但是,我收到错误System.TypeLoadException : ... Could not load type because it contains an object field at offset 0 that is incorrectly aligned or overlapped by a non-object field.
使用以下代码。 我相信它可能是字符串之一或整数数组(变量B7)? 我将如何改变它以使其工作 – 我是否必须将整数数组分解为多个变量?
[StructLayoutAttribute(LayoutKind.Explicit)] public struct Union{ [FieldOffset(0)] public A a; [FieldOffset(0)] public B b; [FieldOffset(0)] public C c; [FieldOffset(0)] public D d; } [StructLayout(LayoutKind.Sequential)] public struct A { public int A1; public int A2; public int A3; [MarshalAs(UnmanagedType.LPTStr, SizeConst = 17)] public string A4; [MarshalAs(UnmanagedType.LPTStr, SizeConst = 4)] public string A5; } [StructLayout(LayoutKind.Sequential)] public struct B { public int B1; [MarshalAs(UnmanagedType.LPTStr, SizeConst = 2)] public string B2; [MarshalAs(UnmanagedType.LPTStr, SizeConst = 4)] public string B3; [MarshalAs(UnmanagedType.LPTStr, SizeConst = 6)] public string B4; public int B5; public int B6; [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.U4, SizeConst = 255)] public int[] B7; } [StructLayout(LayoutKind.Sequential)] public struct C { public int C1; public int C2; public int C3; public int C4; [MarshalAs(UnmanagedType.LPTStr, SizeConst = 32)] public string C5; public float C6; public float C7; public float C8; public float C9; public float C10; public float C11; public float C12; public float C13; public float C14; } [StructLayout(LayoutKind.Sequential)] public struct D { public int D1; [MarshalAs(UnmanagedType.LPTStr, SizeConst = 36)] public string D2; }
只需直接使用A / B / C / D结构并跳过联合。 在您的extern调用中,只需在方法声明中替换正确的结构。
extern void UnionMethodExpectingA( A a );
如果非托管方法实际上接受一个联合并且基于传递的类型表现不同,那么您可以声明所有最终调用相同非托管入口点的不同外部方法。
[DllImport( "unmanaged.dll", EntryPoint="ScaryMethod" )] extern void ScaryMethodExpectingA( A a ); [DllImport( "unmanaged.dll", EntryPoint="ScaryMethod" )] extern void ScaryMethodExpectingB( B b );
更新了“长度”参数。 逻辑仍然适用。 只需创建一个“包装器”方法并执行相同的操作。
void CallScaryMethodExpectingA( A a ) { ScaryMethodExpectingA( a, Marshal.SizeOf( a ) ); }
如果不知道你想要实现什么,就很难回答这个问题。 对于任何正常的用例,明确布局的结构是一个非常糟糕的选择; 这只有在使用本机调用(pinvoke)中的数据时才有意义,在这种情况下,您肯定不想使用托管类string
。 [MarshalAs]
属性仅在调用调用期间生效,而不是每次从托管代码读取或写入字段时都会生效。 它不允许您将字符串指针与int重叠,因为这样做会允许您将指针设置为无意义的值,然后访问该字符串会使CLR崩溃。 对于数组也是如此,因此您也不能使用char[]
。
如果您是需要调用的本机代码的作者,那么我强烈建议您编写四个单独的方法,而不是一个接受四个完全不同的数据结构的方法。
如果您无法更改本机代码,那么您可以按照现在的方式声明四个结构A
, B
, C
和D
,并且只需直接使用它们,而无需使用联合。 只需为同一本机函数声明四个不同的pinvoke声明(使用[DllImport]
属性上的EntryPoint
属性)。
上述就是C#学习教程:C#中的联合 – 与非对象字段错误对齐或重叠分享的全部内容,如果对大家有所用处且需要了解更多关于C#学习教程,希望大家多多关注—计算机技术网(www.ctvol.com)!
本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。
ctvol管理联系方式QQ:251552304
本文章地址:https://www.ctvol.com/cdevelopment/962081.html