Is there a 64bit alignment requirement for request data structures?

Dear all,

I recently stumbled over the following “server exception 19” issue when spitting one of my request data structures into two distinct structures:

Initially I had:

struct MyStruct {
    ...
    double foo;
    int32 bar;
    int32 barfoo;
    double foobar;
    ...
};

(the ‘int32’ data type here shall illustrate a 32bit integer value, the double type is 64bit)

And the struct was properly declared (registered) with SimConnect:

...
SimConnect_AddToDataDefinition(handle, MY_STRUCT_DEF, SimVar::Foo, "Percent", ::SIMCONNECT_DATATYPE_FLOAT64);
SimConnect_AddToDataDefinition(handle, MY_STRUCT_DEF, SimVar::Bar, "Percent", ::SIMCONNECT_DATATYPE_INT32);
SimConnect_AddToDataDefinition(handle, MY_STRUCT_DEF, SimVar::BarFoo, "Percent", ::SIMCONNECT_DATATYPE_INT32);
SimConnect_AddToDataDefinition(handle, MY_STRUCT_DEF, SimVar::FooBar, "Percent", ::SIMCONNECT_DATATYPE_FLOAT64);
....

Note that the variables before and after are also 64bit in size, and the two 32bit integers also add up to 64bit in total size.

(The fact that I placed them “next to each other” was not totally coincidental, in order for the compiler to be able to properly align the members to 64bit - but the fact that I had an even number of int32 values was coincidental).

So this was working as expected, for both reading and writing this struct.

However after the split into two distinct request structures I had e.g.:

struct MyStruct {
    ...
    double foo;
    int32 bar;
};

So the last member is only 32bit in size (and the only int32 value in that particular record), and hence the total size of the struct is not a multiple of 64bit. While reading this struct was returning the expected results I got a server exception with number 19 when trying to write (= send to FS2020) this struct.

According to

the “number 19 exception” indicates an “invalid data size”.

And indeed, when I simply change the integer to a 64bit integer (and the corresponding SimConnect type to SIMCONNECT_DATATYPE_INT64):

struct MyStruct {
    ...
    double foo;
    int64 bar;
};

then - lo and behold - the write request is successfully executed.

I did not find any specific mentioning of the requirement that request records must have a size a multiple of 64bit (8 byte):

But conveniently all examples that I came across seem to use double values. In fact, SIMCONNECT_DATATYPE_FLOAT64 is the default data type, when not specified otherwise.

So while reading records with SIMCONNECT_DATATYPE_INT32 seems to work setting them does fail with an “invalid size” (19) exception. Unless the INT32s add up to a multiple of 64bit, or so it seems.

I do have a working solution (by making all data align to 64bit respectively by using int64 - even for a silly Boolean variable) but am still curious what is going on here respectively if someone found some documentation which would explain the observed behaviour?

I have not seen that issue here, Steeler. But, I have not done a breakup of that structure either.

Consider creating a post on FSDeveloper. They may have seen or addressed this before. Their skills there as programmers are a little more advanced than most of the users are here. Run it past WarpD and Prepar3dGuy.

1 Like

Just checking … do not forget to define

[StructLayout(LayoutKind.Sequential, Pack = 1)]

No problems like you mention over here either . C# Managed

Greetings

1 Like

That‘s a „C# thingie“, right? Not a SimConnect specific annotation?

I am in the C++ world, so the record (members) are aligned according to the compiler (I‘d have to check whether the C++ standard makes any „recommendations“ here for compilers).

The size of the record is of course evaluated by the C++ sizeof() operator: so however the compiler layouts the struct in memory the overall size that I send to FS2020 (as request) is correct.

However FS2020 apparently does not lile that size respectively there must be a mismatch with the defined types (FLOAT64, INT32 etc.)

So are you saying that you are writing successfully int32 values in your case? An „odd number“ thereof, to be specific?

UPDATE:

Ah indeed, there are (MS specific) compiler options which control the alignment. So it is most likely related to that.

UPDATE 2:

The C++ (and MS compiler specific) equivalent to the above C# layout annotation is most likely:

Geez! It was really the packing of the struct that was the problem here.

For the record, I am using the g++ compiler (which comes “out of the box” with the open source Qt framework, also on Windows), and the corresponding compiler instruction is (in C++) attribute ((packed)), as in:

struct MyStruct {
  // members of various byte sizes
  ...
} __attribute__ ((packed));

(I have yet to figure out and add the proper preprocessor macros, to distinguish between g++ and an msvc, in case the code gets compiled with an MS compiler, also refer to the “pack pragma” above).

A test debug statement confirmed that the “packed” struct is 4 bytes less in size (int32 vs int64) than the memory-aligned one, as expected. And with this also the write requests worked as expected (why the read requests actually resulted in the expected data is still beyond me - pure luck?).

I hope this is of help to anyone else stumbling over the same issue:


→ control the memory alignment (packing) of your structs! (with the corresponding compiler-specific instructions)


UPDATE:

Never mind preprocessor macros: it is probably simply enough to add both (all relevant) compiler-specific instructions, for g++ and MSVC, as e.g. in:

#pragma pack(push, 1)
struct MyStruct {
  // members of various byte sizes
  ...
} __attribute__ ((packed));
#pragma pack(pop)

At least the g++ compiler does not care about the MSVC specific #pragma instruction (in fact, it may even “understand” it, haven’t tried that yet), and I’d expect the MSVC to do the same with the g++ specific __ attribute __ instruction.

Other compilers are not relevant here (for me), because in the end this code compiles on Windows only anyway (SimConnect is Windows only, of course ;))

Of course in the aftermath it is totally obvious that I should have checked the struct alignment beforehand - doh! After all, we are mapping an opaque memory range (received from the SimConnect server, made available via SIMCONNECT_RECV_SIMOBJECT_DATA → dwData member) onto our struct, so of course memory packing is of importance!

But if you, as a C++ developer like me, are still wondering “how should I have known?”, the official documentation indeed quickly mentions it here:

A quick “Some specific information must be provided with each structure, as shown in the C# sample below” and an example - and that’s it.

But alas that is placed in the topic PROGRAMMING SIMCONNECT CLIENTS
USING MANAGED CODE - which C++ programmer is going to read that! :wink:

And it does:

“For compatibility with Microsoft Windows compilers, GCC supports a set of #pragma directives which change the maximum alignment of members of structures (other than zero-width bitfields), unions, and classes subsequently defined.”

:wink:

1 Like