We want to call a C++ function in a x64 application: https://www.inventcom.net/fanuc-focas-library/misc/cnc_diagnoss
We need to pass a ODBDGN struct to the function. This struct has an union in it so in c# we cannot defined union we need to do this:
[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;
}
In 32bits application, initializing this struct works fine.
Int 64bits application, initializing this struct doesn't work and throw this error:
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
I understand in x64 application, default Pack value is 8 so offset must be 8 not 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;
}
But if we call the cnc_diagnoss C++ function, in the struct we are missing the 2 octet beetween type
and cdatas
But when we declare the struct like this:
[StructLayout(LayoutKind.Sequential)]
public class ODBDGN_Byte
{
public short datano;
public short type;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_AXIS)]
public byte[] cdatas;
}
this works (not the union but if the data is byte) and there nothing missing between type
and cdatas
In this case, datano
is offset 0 type
is offet 2 and cdatas
is offset 4
So why can we not create explicit layout in that case? I don't understand how this works...
The problem here is a common one. Your basic rule of thumb is that if ever you use FieldOffset
you do so with an offset value of 0
. In other words you replicate the C++ union. This allows you to map most closely to the way the C++ types are declared and allows the p/invoke marsaller to align your structures properly, without you having to do so manually. It does mean introducing an extra type, but it is worth it. It goes like this:
[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;
}
Personally though I don't think that using a union is really necessary here. Another option is to declare it like this:
[StructLayout(LayoutKind.Sequential)]
public class ODBDGN
{
public short datano;
public short type;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_AXIS)]
public byte[] code;
}
And then add helper methods to the class that allowed the user to get and set the code field with arrays of other base types. The helper methods would convert between short[]
and byte[]
, and between int[]
and byte[]
.
User contributions licensed under CC BY-SA 3.0