x64 C#应用程序中的结构中的FieldsOffset

时间:2018-10-25 08:31:52

标签: c# struct 64-bit dllimport

我们要在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 ++函数,则在结构中,我们缺少typecdatas之间的2个八位位组

但是当我们这样声明结构时:

[StructLayout(LayoutKind.Sequential)]
        public class ODBDGN_Byte
        {
            public short datano;
            public short type;     
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_AXIS)]
            public byte[] cdatas;
        }

这有效(不是联合,但如果数据为字节),则typecdatas之间没有任何缺失

在这种情况下,datano的偏移量为0 type的偏移量为2,而cdatas的偏移量为4

那么在这种情况下为什么不能创建显式布局?我不明白这是怎么回事...

1 个答案:

答案 0 :(得分:1)

这里的问题很常见。您的基本经验法则是,如果您使用FieldOffset,则偏移量为0。换句话说,您将复制C ++联合。这使您可以最紧密地映射到C ++类型的声明方式,并允许p / invoke marsaller正确对齐结构,而无需手动进行。这确实意味着要引入额外的类型,但这是值得的。它是这样的:

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

然后将帮助程序方法添加到该类,该方法允许用户使用其他基本类型的数组获取并设置代码字段。辅助方法将在short[]byte[]之间以及int[]byte[]之间转换。