There was a problem with alignment of structures. There is a code (not mine, this code is compiled under x64) in C ++:

#ifndef ETS2_TELEMETRY_COMMON_HPP #define ETS2_TELEMETRY_COMMON_HPP // This file contains "Common definitions" for this ETS2 telemetry plug-in. // This includes: // - Debug logging detail options // - Shared memory map struct layout // - [..] #define ETS2_PLUGIN_REVID 5 #define ETS2_PLUGIN_LOGGING_ON 0 #define ETS2_PLUGIN_LOGGING_SHAREDMEMORY 0 #define ETS2_PLUGIN_FILENAME_PREFIX "C:\ets2telem_" #if ETS2_PLUGIN_LOGGING_ON == 1 #define SDK_ENABLE_LOGGING #endif #define ETS2_PLUGIN_MMF_NAME TEXT("Local\\SimTelemetryETS2") #define ETS2_PLUGIN_MMF_SIZE (16*1024) #define TRUCK_STRING_OFFSET 15*1024 #define TRAILER_STRING_OFFSET TRUCK_STRING_OFFSET+64 typedef struct ets2TelemetryMap_s { unsigned int time; unsigned int paused; struct { unsigned int ets2_telemetry_plugin_revision; unsigned int ets2_version_major; unsigned int ets2_version_minor; } tel_revId; // All variables per revision are packed into 1 struct. // Newer revisions must contain identical struct layouts/lengths, even if variabeles become deprecated. // Replaced/new variabeles should be added in seperate structs struct { bool engine_enabled; bool trailer_attached; // vehicle dynamics float speed; float accelerationX; float accelerationY; float accelerationZ; float coordinateX; float coordinateY; float coordinateZ; float rotationX; float rotationY; float rotationZ; // drivetrain essentials int gear; int gears; int gearRanges; int gearRangeActive; float engineRpm; float engineRpmMax; float fuel; float fuelCapacity; float fuelRate; // ! Not working float fuelAvgConsumption; // user input float userSteer; float userThrottle; float userBrake; float userClutch; float gameSteer; float gameThrottle; float gameBrake; float gameClutch; // truck & trailer float truckWeight; float trailerWeight; int modelType[2]; int trailerType[2]; // ! deprecated } tel_rev1; struct { long time_abs; int gears_reverse; // Trailer ID & display name float trailerMass; char trailerId[64]; char trailerName[64]; // Job information int jobIncome; int time_abs_delivery; char citySrc[64]; char cityDst[64]; char compSrc[64]; char compDst[64]; } tel_rev2; struct { int retarderBrake; int shifterSlot; int shifterToggle; int fill; bool cruiseControl; bool wipers; bool parkBrake; bool motorBrake; bool electricEnabled; bool engineEnabled; bool blinkerLeftActive; bool blinkerRightActive; bool blinkerLeftOn; bool blinkerRightOn; bool lightsParking; bool lightsBeamLow; bool lightsBeamHigh; bool lightsAuxFront; bool lightsAuxRoof; bool lightsBeacon; bool lightsBrake; bool lightsReverse; bool batteryVoltageWarning; bool airPressureWarning; bool airPressureEmergency; bool adblueWarning; bool oilPressureWarning; bool waterTemperatureWarning; float airPressure; float brakeTemperature; int fuelWarning; float adblue; float adblueConsumption; float oilPressure; float oilTemperature; float waterTemperature; float batteryVoltage; float lightsDashboard; float wearEngine; float wearTransmission; float wearCabin; float wearChassis; float wearWheels; float wearTrailer; float truckOdometer; float cruiseControlSpeed; // General info about the truck etc; char truckMake[64]; char truckMakeId[64]; char truckModel[64]; } tel_rev3; struct { float speedLimit; float routeDistance; float routeTime; float fuelRange; float gearRatiosForward[24]; float gearRatiosReverse[8]; float gearDifferential; int gearDashboard; } tel_rev4; // added in sdk1.5 struct { bool onJob; bool jobFinished; } tel_rev5; } ets2TelemetryMap_t; #endif 

And there is the following code in C # (also not mine) that gets this structure.

 [StructLayout(LayoutKind.Explicit)] public unsafe struct Ets2SdkData { [FieldOffset(0)] public uint time; [FieldOffset(4)] public uint paused; [FieldOffset(8)] public uint ets2_telemetry_plugin_revision; [FieldOffset(12)] public uint ets2_version_major; [FieldOffset(16)] public uint ets2_version_minor; //***** REVISION 1 ****** // [FieldOffset(20), MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public byte[] flags; //vehicle dynamics [FieldOffset(24)] public float speed; [FieldOffset(28)] public float accelerationX; [FieldOffset(32)] public float accelerationY; [FieldOffset(36)] public float accelerationZ; [FieldOffset(40)] public float coordinateX; [FieldOffset(44)] public float coordinateY; [FieldOffset(48)] public float coordinateZ; [FieldOffset(52)] public float rotationX; [FieldOffset(56)] public float rotationY; [FieldOffset(60)] public float rotationZ; //drivetrain essentials [FieldOffset(64)] public int gear; [FieldOffset(68)] public int gears; [FieldOffset(72)] public int gearRanges; [FieldOffset(76)] public int gearRangeActive; [FieldOffset(80)] public float engineRpm; [FieldOffset(84)] public float engineRpmMax; [FieldOffset(88)] public float fuel; [FieldOffset(92)] public float fuelCapacity; [FieldOffset(96)] public float fuelRate; [FieldOffset(100)] public float fuelAvgConsumption; // user input [FieldOffset(104)] public float userSteer; [FieldOffset(108)] public float userThrottle; [FieldOffset(112)] public float userBrake; [FieldOffset(116)] public float userClutch; [FieldOffset(120)] public float gameSteer; [FieldOffset(124)] public float gameThrottle; [FieldOffset(128)] public float gameBrake; [FieldOffset(132)] public float gameClutch; //truck & trailer [FieldOffset(136)] public float truckWeight; [FieldOffset(140)] public float trailerWeight; [FieldOffset(144)] public int modelOffset; [FieldOffset(148)] public int modelLength; [FieldOffset(152)] public int trailerOffset; [FieldOffset(156)] public int trailerLength; //***** REVISION 2 ****** // [FieldOffset(160)] public int timeAbsolute; [FieldOffset(164)] public int gearsReverse; [FieldOffset(168)] public float trailerMass; [FieldOffset(172), MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] public byte[] trailerId; [FieldOffset(236), MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] public byte[] trailerName; [FieldOffset(300)] public int jobIncome; [FieldOffset(304)] public int jobDeadline; [FieldOffset(308), MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] public byte[] jobCitySource; [FieldOffset(372), MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] public byte[] jobCityDestination; [FieldOffset(436), MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] public byte[] jobCompanySource; [FieldOffset(500), MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] public byte[] jobCompanyDestination; //***** REVISION 3 ****** // [FieldOffset(564)] public int retarderBrake; [FieldOffset(568)] public int shifterSlot; [FieldOffset(572)] public int shifterToggle; [FieldOffset(576)] public int fill; [FieldOffset(580), MarshalAs(UnmanagedType.ByValArray, SizeConst = 24)] public byte[] aux; [FieldOffset(604)] public float airPressure; [FieldOffset(608)] public float brakeTemperature; [FieldOffset(612)] public int fuelWarning; [FieldOffset(616)] public float adblue; [FieldOffset(620)] public float adblueConsumption; [FieldOffset(624)] public float oilPressure; [FieldOffset(628)] public float oilTemperature; [FieldOffset(632)] public float waterTemperature; [FieldOffset(636)] public float batteryVoltage; [FieldOffset(640)] public float lightsDashboard; [FieldOffset(644)] public float wearEngine; [FieldOffset(648)] public float wearTransmission; [FieldOffset(652)] public float wearCabin; [FieldOffset(656)] public float wearChassis; [FieldOffset(660)] public float wearWheels; [FieldOffset(664)] public float wearTrailer; [FieldOffset(668)] public float truckOdometer; [FieldOffset(672)] public float cruiseControlSpeed; [FieldOffset(676), MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] public byte[] truckMake; [FieldOffset(740), MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] public byte[] truckMakeId; [FieldOffset(804), MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] public byte[] truckModel; // ***** REVISION 4 ****** // [FieldOffset(868)] public float speedLimit; [FieldOffset(872)] public float routeDistance; [FieldOffset(876)] public float routeTime; [FieldOffset(880)] public float fuelRange; [FieldOffset(884), MarshalAs(UnmanagedType.ByValArray, SizeConst = 24)] public float[] gearRatioForward; [FieldOffset(980), MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] public float[] gearRatioReverse; [FieldOffset(1012)] public float gearRatioDifferential; [FieldOffset(1016)] public int gearDashboard; [FieldOffset(1020)] public byte onJob; [FieldOffset(1021)] public byte jobFinished; public bool GetBool(Ets2SdkBoolean i) { if (i == Ets2SdkBoolean.TrailerAttached) return flags[1] > 0; return aux[(int)i] > 0; } } 

This code in C # works if the target platform is set to "x86" in the project properties, but if you change it to "x64" (which I need to do), then when executing the code, errors like this immediately begin:

Failed to load type "Ets2SdkData2" ... because it contains an object field with an offset of 20, which is incorrectly aligned or overlapped by a field that does not represent an object. "

From the description of the error, as I understood that " FieldOffset " is not correctly specified on all variables of the type " byte []".

Here the question arises: if for a project with a target platform - x86, the "FieldOffset" property is set correctly everywhere and the program receives all the data, then what should it be "FieldOffset" in the project with a target platform - x64?

    2 answers 2

    Offsets of fields in this case do not depend on the platform, because there is not a single pointer in the structure. But the fact is that .NET for some reason does not allow working with incorrectly aligned arrays in structures when using LayoutKind.Explicit. You need to either use StructLayout(LayoutKind.Sequential, Pack = 1) and get rid of explicit offsets, or turn on unsafe code and declare all arrays like this:

     [FieldOffset(20)] public fixed byte flags[4]; 

    Full code:

     using System; using System.Runtime.InteropServices; using System.Text; namespace ConsoleApp1 { class Program { public static T ToObject<T>(byte[] structureDataBytes) { T createdObject = default(T); var memoryObjectSize = Marshal.SizeOf(typeof(T)); // Cannot create object from array that is too small. if (memoryObjectSize > structureDataBytes.Length) return createdObject; // Reserve unmanaged memory, copy structureDataBytes bytes to there, and convert this unmanaged memory to a managed type. // Then free memory. var reservedMemPtr = Marshal.AllocHGlobal(memoryObjectSize); Marshal.Copy(structureDataBytes, 0, reservedMemPtr, memoryObjectSize); createdObject = (T)Marshal.PtrToStructure(reservedMemPtr, typeof(T)); Marshal.FreeHGlobal(reservedMemPtr); return createdObject; } static void Main(string[] args) { byte[] arr = new byte[2000]; arr[0] = 1; arr[1] = 0; arr[2] = 0; arr[3] = 0; Ets2SdkData data = ToObject<Ets2SdkData>(arr); Console.WriteLine(data.time.ToString()); Console.ReadKey(); } } [StructLayout(LayoutKind.Explicit, Pack = 1)] public unsafe struct Ets2SdkData { [FieldOffset(0)] public uint time; [FieldOffset(4)] public uint paused; [FieldOffset(8)] public uint ets2_telemetry_plugin_revision; [FieldOffset(12)] public uint ets2_version_major; [FieldOffset(16)] public uint ets2_version_minor; //***** REVISION 1 ****** // [FieldOffset(20)] public fixed byte flags[4]; //vehicle dynamics [FieldOffset(24)] public float speed; [FieldOffset(28)] public float accelerationX; [FieldOffset(32)] public float accelerationY; [FieldOffset(36)] public float accelerationZ; [FieldOffset(40)] public float coordinateX; [FieldOffset(44)] public float coordinateY; [FieldOffset(48)] public float coordinateZ; [FieldOffset(52)] public float rotationX; [FieldOffset(56)] public float rotationY; [FieldOffset(60)] public float rotationZ; //drivetrain essentials [FieldOffset(64)] public int gear; [FieldOffset(68)] public int gears; [FieldOffset(72)] public int gearRanges; [FieldOffset(76)] public int gearRangeActive; [FieldOffset(80)] public float engineRpm; [FieldOffset(84)] public float engineRpmMax; [FieldOffset(88)] public float fuel; [FieldOffset(92)] public float fuelCapacity; [FieldOffset(96)] public float fuelRate; [FieldOffset(100)] public float fuelAvgConsumption; // user input [FieldOffset(104)] public float userSteer; [FieldOffset(108)] public float userThrottle; [FieldOffset(112)] public float userBrake; [FieldOffset(116)] public float userClutch; [FieldOffset(120)] public float gameSteer; [FieldOffset(124)] public float gameThrottle; [FieldOffset(128)] public float gameBrake; [FieldOffset(132)] public float gameClutch; //truck & trailer [FieldOffset(136)] public float truckWeight; [FieldOffset(140)] public float trailerWeight; [FieldOffset(144)] public int modelOffset; [FieldOffset(148)] public int modelLength; [FieldOffset(152)] public int trailerOffset; [FieldOffset(156)] public int trailerLength; //***** REVISION 2 ****** // [FieldOffset(160)] public int timeAbsolute; [FieldOffset(164)] public int gearsReverse; [FieldOffset(168)] public float trailerMass; [FieldOffset(172)] public fixed byte trailerId[64]; [FieldOffset(236)] public fixed byte trailerName[64]; [FieldOffset(300)] public int jobIncome; [FieldOffset(304)] public int jobDeadline; [FieldOffset(308)] public fixed byte jobCitySource[64]; [FieldOffset(372)] public fixed byte jobCityDestination[64]; [FieldOffset(436)] public fixed byte jobCompanySource[64]; [FieldOffset(500)] public fixed byte jobCompanyDestination[64]; //***** REVISION 3 ****** // [FieldOffset(564)] public int retarderBrake; [FieldOffset(568)] public int shifterSlot; [FieldOffset(572)] public int shifterToggle; [FieldOffset(576)] public int fill; [FieldOffset(580)] public fixed byte aux[24]; [FieldOffset(604)] public float airPressure; [FieldOffset(608)] public float brakeTemperature; [FieldOffset(612)] public int fuelWarning; [FieldOffset(616)] public float adblue; [FieldOffset(620)] public float adblueConsumption; [FieldOffset(624)] public float oilPressure; [FieldOffset(628)] public float oilTemperature; [FieldOffset(632)] public float waterTemperature; [FieldOffset(636)] public float batteryVoltage; [FieldOffset(640)] public float lightsDashboard; [FieldOffset(644)] public float wearEngine; [FieldOffset(648)] public float wearTransmission; [FieldOffset(652)] public float wearCabin; [FieldOffset(656)] public float wearChassis; [FieldOffset(660)] public float wearWheels; [FieldOffset(664)] public float wearTrailer; [FieldOffset(668)] public float truckOdometer; [FieldOffset(672)] public float cruiseControlSpeed; [FieldOffset(676)] public fixed byte truckMake[64]; [FieldOffset(740)] public fixed byte truckMakeId[64]; [FieldOffset(804)] public fixed byte truckModel[64]; // ***** REVISION 4 ****** // [FieldOffset(868)] public float speedLimit; [FieldOffset(872)] public float routeDistance; [FieldOffset(876)] public float routeTime; [FieldOffset(880)] public float fuelRange; [FieldOffset(884)] public fixed float gearRatioForward[24]; [FieldOffset(980)] public fixed float gearRatioReverse[8]; [FieldOffset(1012)] public float gearRatioDifferential; [FieldOffset(1016)] public int gearDashboard; [FieldOffset(1020)] public byte onJob; [FieldOffset(1021)] public byte jobFinished; } } 
    • Option 1 is eliminated (because with such options all variables get values ​​either 0 or "null", I don’t know why), and the second option gives an error: Unable to pack the type "Ets2SdkData" as an unmanaged structure; it is impossible to calculate the size or offset that makes sense. "Such an error occurs when: var memoryObjectSize = Marshal.SizeOf (typeof (T)); - keinHerz
    • @keinHerz and on which specific line of code gives an error? - MSDN.WhiteKnight
    • This code does not fit here. Therefore, here is (11 line): pastebin.com/gTk0ivHV - keinHerz
    • @keinHerz Yes, like I have nothing of the kind. Added full code in response. - MSDN.WhiteKnight
    • "For some reason" ?! The reason is well-known: the pointer to the managed object (and the array is exactly like this!) Must be aligned. - Pavel Mayorov

    To work with binary serialization, you need to ensure that C ++ will generate the same structure with any compilation options. By default, the size of the structure does not have to coincide with the sum of the sizes of all fields, since All structure fields are aligned to their size. For example, the int16_t field must be at the offset of a multiple of 2, int32_t is a multiple of 4, and so on.

    In your example, the fields engine_enabled and trailer_attached are of type bool, which takes up a byte each, but the speed field is 4 bytes in size and the compiler will try to place it with an offset of 4 from the beginning of the tel_rev1 structure, that is, between two bulls and float there will be an extra hole in 2 bytes.

    Further, int and long types in different architectures can have different sizes, which is normal for serialization, replace them with fixed-size types, for example, int32_t

    Next, in order to be sure that the structure "did not float", it is necessary to insert static_assert with checking dimensions and displacements. Type of such:

     typedef struct ets2TelemetryMap_s { ... }; static_assert(sizeof(ets2TelemetryMap_s) == 1024, "error"); // И сколько там должно быть? static_assert(sizeof(ets2TelemetryMap_s::tel_revId) == 12, "error"); .. static_assert(sizeof(ets2TelemetryMap_s::tel_rev4) == 152, "error"); static_assert(offsetof(ets2TelemetryMap_s::tel_revId) == 8, "error") // <-- проверяем что ключевые поля лежат по правильным смещениям ... static_assert(offsetof(ets2TelemetryMap_s, tel_rev5.jobFinished) == 1021, "error") 

    After this, you can use binary serialization, about big-endian and litle-endian we do not forget.

    PS #pragma pack (1) I do not advise, bad practice for this project. IMHO

    • This is all wonderful, to know even with ++) - keinHerz
    • @keinHerz Then do it simply: replace all "int" and "long" with "int32_t", if it works, then ok, if not, replace all "bool" with "int32_t". But such replacements (bool on in32_t) can affect C ++ code, if the code was written “normal”, then it should work. - ffk
    • Well, what to do with the char charts, they also swear at them in the C # code: [FieldOffset (172), MarshalAs (UnmanagedType.U1, SizeConst = 64)] public byte [] trailerId; .In C ++ code is: char trailerId [64]; - keinHerz
    • @keinHerz do nothing, should work, there seems to be all arrays multiple of 8, so everything should be ok. If it didn’t work, then see what your sizeof (ets2TelemetryMap_s) is under x86 and under x64 - ffk
    • No, this option is not a ride. All without changes. The code on the pros is a library that works as an SDK in the game. - keinHerz