FieldsOffset в структурах в приложении x64 C # - PullRequest
0 голосов
/ 25 октября 2018

Мы хотим вызвать функцию C ++ в приложении x64: 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 значение пакета по умолчанию равно 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 ++, в структуре нам не хватает 2 октета между 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

Так почему же мы не можем создать явный макет в этом случае?Я не понимаю, как это работает ...

1 Ответ

0 голосов
/ 25 октября 2018

Проблема здесь обычная.Основное правило заключается в том, что если вы когда-либо используете 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;
}

И затем добавить вспомогательные методы в класс, который позволил пользователю получить и установить поле кода с массивами других базовых типов.Вспомогательные методы конвертируются между short[] и byte[] и между int[] и byte[].

...