SimConnect + WASM combined project using VS2019

Addendum 3: Packing structures

I had an issue with a structure I was using between my WASM Module and my SimConnect Client. When I was changing one of the datatypes from UInt16 to double, then suddenly my implementation stopped working and even completely crashed.

Below is the originally structure in both my WASM Module and SimConnect Client where all worked fine (this structure is not used in the implementation mentioned in this tutorial, but for a newer version for which I will soon create a new post).

WASM Module:

struct LVar {
	UINT16 lvID;
	UINT16 DefineID;
	UINT16 Offset;
	UINT16 Value;
	char Name[MESSAGE_SIZE];
	char Event[EVENT_NAME_SIZE];
};

SimConnect Client:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public struct LVarAck
{
    public UInt16 lvID;
    public UInt16 DefineID;
    public UInt16 Offset;
    public UInt16 Value;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
    public String str;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
    public String sEvent;
};

But when I changed the field “Value” into a FLOAT64 (double), things got wrong.

WASM Module:

struct LVar {
	UINT16 lvID;
	UINT16 DefineID;
	UINT16 Offset;
	FLOAT64 Value;
	char Name[MESSAGE_SIZE];
	char Event[EVENT_NAME_SIZE];
};

SimConnect Client:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public struct LVarAck
{
    public UInt16 lvID;
    public UInt16 DefineID;
    public UInt16 Offset;
    public double Value;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
    public String str;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
    public String sEvent;
};

It took a while for me to realize that the issue was caused by the setting “Pack = 1”. Here you can read the full story what this pack exactly does.

Bottom line is that by default, the fields in a structure are using an alignment that is defined by the settings of the compiler. In my compilers in VS2019 this is by default set to “Pack = 8”. You can read the details in the above mentioned link, but the result is that memory mapping of the LVar structure in my WASM Module looks like this:

Address 0: UINT16 lvID
Address 2: UINT16 DefineID
Address 4: UINT16 Offset
Address 6: padding             < this padding is automatically added to align at 8 bytes
Address 8: FLOAT64 Value
Address 16: ...

But in the SimConnect Client, the packing was forced to use “pack = 1”, which means that the below structure is expected:

Address 0: UInt16 lvID
Address 2: UInt16 DefineID
Address 4: UInt16 Offset
Address 6: double Value < HERE THE MISALINGMENT STARTS
Address 14: ...

Because “Pack = 1”, there are no gaps or padding used. You can clearly see the misalignment of 2 bytes, because no padding is added. If I used a UInt16 in both structures for the field Value, all worked well, but this is pure coincidence!

A way to solve this is to instruct the C++ compiler also to use “pack = 1”, but you need to use #pragma directives for this.

#pragma pack(push, 1) // packing is now 1
struct LVar {
	UINT16 lvID;
	UINT16 DefineID;
	UINT16 Offset;
	FLOAT64 Value;
	char Name[MESSAGE_SIZE];
	char Event[EVENT_NAME_SIZE];
};
#pragma pack(pop) // packing is 8 again

You could also remove the “pack = 1” in C#, in which case the #pragma directive in C++ isn’t necessary, because both compilers then use the (same) default packing. But in my opinion, it is safer to explicitly control the packing of the structures, because you never know what the default is.

Don’t hesitate to comment if you think I’m wrong :nerd_face:

2 Likes