x64 C# 应用程序中结构中的 FieldsOffset
FieldsOffset in structs in x64 C# application
我们想在 x64 应用程序中调用 C++ 函数:https://www.inventcom.net/fanuc-focas-library/misc/cnc_diagnoss
我们需要将 ODBDGN 结构传递给函数。这个结构中有一个联合,所以在 c# 中我们不能定义联合,我们需要这样做:
[StructLayout(LayoutKind.Explicit, Pack=4)]
public class ODBDGN
{
[FieldOffset(0)]
public short datano; /* data number */
[FieldOffset(2)]
public short type; /* axis number */
[FieldOffset(4),
MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_AXIS)]
public byte[] cdatas;
[FieldOffset(4),
MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_AXIS)]
public short[] idatas;
[FieldOffset(4),
MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_AXIS)]
public int[] ldatas;
}
在 32 位应用程序中,初始化此结构工作正常。
Int 64 位应用程序,初始化此结构不起作用并抛出此错误:
System.TypeLoadException
HResult=0x80131522
Message=Impossible de charger le type 'ODBDGN' à partir de l'assembly 'CommunicationFanuc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null', car il contient un champ objet à l'offset '4' qui n'est pas correctement aligné ou qui est chevauché par un champ non objet.
Source=CommunicationFanuc
StackTrace:
at CommunicationFanuc.DiagnosticAddress.Read() in D:\Projets\PMM2.0\CommunicationFanuc\Addresses\Channel\Diagnostic\DiagnosticAddress.cs:line 98
at CommunicationFanuc.Tests.PtmFocasEthernet.ReadDiagnostic() in D:\Projets\PMM2.0\CommunicationFanuc.Tests\Ethernet\PtmFocasEthernet.cs:line 809
我了解在 x64 应用程序中,默认 Pack 值是 8,所以偏移量必须是 8 而不是 4 :
[StructLayout(LayoutKind.Explicit)]
public class ODBDGN
{
[FieldOffset(0)]
public short datano; /* data number */
[FieldOffset(2)]
public short type; /* axis number */
[FieldOffset(8),
MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_AXIS)]
public byte[] cdatas;
[FieldOffset(8),
MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_AXIS)]
public short[] idatas;
[FieldOffset(8),
MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_AXIS)]
public int[] ldatas;
}
但是如果我们调用 cnc_diagnoss C++ 函数,在结构中我们缺少 type
和 cdatas
之间的 2 个八位字节
但是当我们这样声明结构时:
[StructLayout(LayoutKind.Sequential)]
public class ODBDGN_Byte
{
public short datano;
public short type;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_AXIS)]
public byte[] cdatas;
}
这有效(不是联合,但如果数据是字节)并且 type
和 cdatas
之间没有任何遗漏
在这种情况下,datano
是偏移量 0 type
是偏移量 2,cdatas
是偏移量 4
那么为什么我们不能在那种情况下创建显式布局呢?我不明白这是怎么回事...
这里的问题很常见。您的基本经验法则是,如果您使用 FieldOffset
,您将使用 0
的偏移值。换句话说,您复制了 C++ 联合。这使您可以最接近地映射到 C++ 类型的声明方式,并允许 p/invoke 编组器正确对齐您的结构,而无需您手动执行此操作。它确实意味着引入一个额外的类型,但这是值得的。它是这样的:
[StructLayout(LayoutKind.Explicit)]
public struct ODBDGN_CODE
{
[FieldOffset(0), MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_AXIS)]
public byte[] cdatas;
[FieldOffset(0), MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_AXIS)]
public short[] idatas;
[FieldOffset(0), MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_AXIS)]
public int[] ldatas;
}
[StructLayout(LayoutKind.Sequential)]
public class ODBDGN
{
public short datano;
public short type;
public ODBDGN_CODE code;
}
虽然我个人并不认为这里真的有必要使用联合。另一种选择是像这样声明它:
[StructLayout(LayoutKind.Sequential)]
public class ODBDGN
{
public short datano;
public short type;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_AXIS)]
public byte[] code;
}
然后向 class 添加辅助方法,允许用户使用其他基本类型的数组获取和设置代码字段。辅助方法将在 short[]
和 byte[]
之间以及 int[]
和 byte[]
之间转换。
我们想在 x64 应用程序中调用 C++ 函数:https://www.inventcom.net/fanuc-focas-library/misc/cnc_diagnoss
我们需要将 ODBDGN 结构传递给函数。这个结构中有一个联合,所以在 c# 中我们不能定义联合,我们需要这样做:
[StructLayout(LayoutKind.Explicit, Pack=4)]
public class ODBDGN
{
[FieldOffset(0)]
public short datano; /* data number */
[FieldOffset(2)]
public short type; /* axis number */
[FieldOffset(4),
MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_AXIS)]
public byte[] cdatas;
[FieldOffset(4),
MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_AXIS)]
public short[] idatas;
[FieldOffset(4),
MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_AXIS)]
public int[] ldatas;
}
在 32 位应用程序中,初始化此结构工作正常。
Int 64 位应用程序,初始化此结构不起作用并抛出此错误:
System.TypeLoadException
HResult=0x80131522
Message=Impossible de charger le type 'ODBDGN' à partir de l'assembly 'CommunicationFanuc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null', car il contient un champ objet à l'offset '4' qui n'est pas correctement aligné ou qui est chevauché par un champ non objet.
Source=CommunicationFanuc
StackTrace:
at CommunicationFanuc.DiagnosticAddress.Read() in D:\Projets\PMM2.0\CommunicationFanuc\Addresses\Channel\Diagnostic\DiagnosticAddress.cs:line 98
at CommunicationFanuc.Tests.PtmFocasEthernet.ReadDiagnostic() in D:\Projets\PMM2.0\CommunicationFanuc.Tests\Ethernet\PtmFocasEthernet.cs:line 809
我了解在 x64 应用程序中,默认 Pack 值是 8,所以偏移量必须是 8 而不是 4 :
[StructLayout(LayoutKind.Explicit)]
public class ODBDGN
{
[FieldOffset(0)]
public short datano; /* data number */
[FieldOffset(2)]
public short type; /* axis number */
[FieldOffset(8),
MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_AXIS)]
public byte[] cdatas;
[FieldOffset(8),
MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_AXIS)]
public short[] idatas;
[FieldOffset(8),
MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_AXIS)]
public int[] ldatas;
}
但是如果我们调用 cnc_diagnoss C++ 函数,在结构中我们缺少 type
和 cdatas
但是当我们这样声明结构时:
[StructLayout(LayoutKind.Sequential)]
public class ODBDGN_Byte
{
public short datano;
public short type;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_AXIS)]
public byte[] cdatas;
}
这有效(不是联合,但如果数据是字节)并且 type
和 cdatas
在这种情况下,datano
是偏移量 0 type
是偏移量 2,cdatas
是偏移量 4
那么为什么我们不能在那种情况下创建显式布局呢?我不明白这是怎么回事...
这里的问题很常见。您的基本经验法则是,如果您使用 FieldOffset
,您将使用 0
的偏移值。换句话说,您复制了 C++ 联合。这使您可以最接近地映射到 C++ 类型的声明方式,并允许 p/invoke 编组器正确对齐您的结构,而无需您手动执行此操作。它确实意味着引入一个额外的类型,但这是值得的。它是这样的:
[StructLayout(LayoutKind.Explicit)]
public struct ODBDGN_CODE
{
[FieldOffset(0), MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_AXIS)]
public byte[] cdatas;
[FieldOffset(0), MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_AXIS)]
public short[] idatas;
[FieldOffset(0), MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_AXIS)]
public int[] ldatas;
}
[StructLayout(LayoutKind.Sequential)]
public class ODBDGN
{
public short datano;
public short type;
public ODBDGN_CODE code;
}
虽然我个人并不认为这里真的有必要使用联合。另一种选择是像这样声明它:
[StructLayout(LayoutKind.Sequential)]
public class ODBDGN
{
public short datano;
public short type;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_AXIS)]
public byte[] code;
}
然后向 class 添加辅助方法,允许用户使用其他基本类型的数组获取和设置代码字段。辅助方法将在 short[]
和 byte[]
之间以及 int[]
和 byte[]
之间转换。