• Which the release of FS2020 we see an explosition of activity on the forun and of course we are very happy to see this. But having all questions about FS2020 in one forum becomes a bit messy. So therefore we would like to ask you all to use the following guidelines when posting your questions:

    • Tag FS2020 specific questions with the MSFS2020 tag.
    • Questions about making 3D assets can be posted in the 3D asset design forum. Either post them in the subforum of the modelling tool you use or in the general forum if they are general.
    • Questions about aircraft design can be posted in the Aircraft design forum
    • Questions about airport design can be posted in the FS2020 airport design forum. Once airport development tools have been updated for FS2020 you can post tool speciifc questions in the subforums of those tools as well of course.
    • Questions about terrain design can be posted in the FS2020 terrain design forum.
    • Questions about SimConnect can be posted in the SimConnect forum.

    Any other question that is not specific to an aspect of development or tool can be posted in the General chat forum.

    By following these guidelines we make sure that the forums remain easy to read for everybody and also that the right people can find your post to answer it.

MSFS Simvars quantity

I've been trying to programm a windows form project, for myself, much line Link2FS. I managed to read around 26 varables from MSFS2020 but as soon as I try to read more those extra variables dont change at all...
Is there a limit to the amount of vars a struct can hold? If so how do I create a second one? For now I've always received the ""System.InvalidCastException: Specified cast is not valid" error on the
Code:
Struct2 struct2 = (Struct2)data.dwData[0];
line

And if I want to read hundreds of vars from the sim, do I really have to include every single one in a line similar to
Code:
   my_simconnect.AddToDataDefinition(DEFINITIONS.Struct1, "COM STANDBY FREQUENCY:1", "MHz", SIMCONNECT_DATATYPE.FLOAT64, 0.0f, SimConnect.SIMCONNECT_UNUSED);
or is there an easier way to read and store hundreds of simvars?
 
I've been trying to programm a windows form project, for myself, much line Link2FS. I managed to read around 26 varables from MSFS2020 but as soon as I try to read more those extra variables dont change at all...
Is there a limit to the amount of vars a struct can hold? If so how do I create a second one? For now I've always received the ""System.InvalidCastException: Specified cast is not valid" error on the
Code:
Struct2 struct2 = (Struct2)data.dwData[0];
line

And if I want to read hundreds of vars from the sim, do I really have to include every single one in a line similar to
Code:
   my_simconnect.AddToDataDefinition(DEFINITIONS.Struct1, "COM STANDBY FREQUENCY:1", "MHz", SIMCONNECT_DATATYPE.FLOAT64, 0.0f, SimConnect.SIMCONNECT_UNUSED);
or is there an easier way to read and store hundreds of simvars?
DId you figure out how to fix this?
 
DId you figure out how to fix this?
Well I don't know if this hellps, but I found a workaround:
For all numeric datatypes (e.g. bool, int, float, etc...) use "SIMCONNECT_DATATYPE.FLOAT64" and declare the vars as "public double "

But I haven't figured out how to declate a second struct...
 

DragonflightDesign

Resource contributor
I've been trying to programm a windows form project, for myself, much line Link2FS. I managed to read around 26 varables from MSFS2020 but as soon as I try to read more those extra variables dont change at all...
Is there a limit to the amount of vars a struct can hold? If so how do I create a second one? For now I've always received the ""System.InvalidCastException: Specified cast is not valid" error on the
Code:
Struct2 struct2 = (Struct2)data.dwData[0];
line

And if I want to read hundreds of vars from the sim, do I really have to include every single one in a line similar to
Code:
   my_simconnect.AddToDataDefinition(DEFINITIONS.Struct1, "COM STANDBY FREQUENCY:1", "MHz", SIMCONNECT_DATATYPE.FLOAT64, 0.0f, SimConnect.SIMCONNECT_UNUSED);
or is there an easier way to read and store hundreds of simvars?
Yes, you do have to AddToDataDefinition every sim var that you want to read. Unless there is a problem with MSFS there is absolutely no reason why you should be restricted to 26 vars in a struct. My P3D SimConnect avionics struct has 50+ members. Also, I have 39 structs where each deals with a separate group of data definitions. 'Public {....} Double' is not a workaround - it is the correct var spec for receiving numeric data. You don't say what you are trying to copy into Struct2 but if it's a string, you must precede every string with a <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=256)>.

I see you are using C#; for reasons stretching back decades now I prefer to use vb.net for managed programs. The vb.Net call to dump incoming data into a struct is:
Code:
Dim cabin As Cabin_Data = CType(data.dwData(0), Cabin_Data)
where 'Cabin_Data' is the struct and 'cabin' is the struct name. Si I'd expect the c# call to look something like
Code:
Cabin_Data cabin = (Cabin_Data)data.dwData(0);
 
I've been trying to get some strings from MSFS and put them on a separate struct.

If I try to get only one string it kind of works but I don't get any text (string is empty). As soon as I try to get more strings i get a System.AccessViolationException.
I don't know what else I can do to make it work...

C#:
...

                my_simconnect.AddToDataDefinition(DEFINITIONS.Struct1, "GPS APPROACH TIMEZONE DEVIATION", "seconds", SIMCONNECT_DATATYPE.FLOAT64, 0.0f, SimConnect.SIMCONNECT_UNUSED);
                my_simconnect.AddToDataDefinition(DEFINITIONS.Struct1, "GPS APPROACH WP INDEX", "number", SIMCONNECT_DATATYPE.FLOAT64, 0.0f, SimConnect.SIMCONNECT_UNUSED);
                my_simconnect.AddToDataDefinition(DEFINITIONS.Struct1, "GPS APPROACH WP COUNT", "number", SIMCONNECT_DATATYPE.FLOAT64, 0.0f, SimConnect.SIMCONNECT_UNUSED);
                my_simconnect.AddToDataDefinition(DEFINITIONS.Struct1, "GPS TARGET DISTANCE", "meters", SIMCONNECT_DATATYPE.FLOAT64, 0.0f, SimConnect.SIMCONNECT_UNUSED);
                my_simconnect.AddToDataDefinition(DEFINITIONS.Struct1, "GPS TARGET ALTITUDE", "meters", SIMCONNECT_DATATYPE.FLOAT64, 0.0f, SimConnect.SIMCONNECT_UNUSED);

                my_simconnect.RegisterDataDefineStruct<Struct1>(DEFINITIONS.Struct1);

                my_simconnect.AddToDataDefinition(DEFINITIONS.StructString, "ATC AIRLINE", "number", SIMCONNECT_DATATYPE.STRING64, 0.0f, SimConnect.SIMCONNECT_UNUSED);
                my_simconnect.AddToDataDefinition(DEFINITIONS.StructString, "ATC FLIGHT NUMBER", "number", SIMCONNECT_DATATYPE.STRING64, 0.0f, SimConnect.SIMCONNECT_UNUSED);
                my_simconnect.AddToDataDefinition(DEFINITIONS.StructString, "ATC ID", "number", SIMCONNECT_DATATYPE.STRING64, 0.0f, SimConnect.SIMCONNECT_UNUSED);
                my_simconnect.AddToDataDefinition(DEFINITIONS.StructString, "ATC MODEL", "number", SIMCONNECT_DATATYPE.STRING64, 0.0f, SimConnect.SIMCONNECT_UNUSED);
                my_simconnect.AddToDataDefinition(DEFINITIONS.StructString, "ATC TYPE", "number", SIMCONNECT_DATATYPE.STRING64, 0.0f, SimConnect.SIMCONNECT_UNUSED);
                my_simconnect.AddToDataDefinition(DEFINITIONS.StructString, "ATC APPROACH AIRPORT ID", "number", SIMCONNECT_DATATYPE.STRING64, 0.0f, SimConnect.SIMCONNECT_UNUSED);
                my_simconnect.AddToDataDefinition(DEFINITIONS.StructString, "ATC GPS APPROACH APPROACH ID", "number", SIMCONNECT_DATATYPE.STRING64, 0.0f, SimConnect.SIMCONNECT_UNUSED);
                my_simconnect.AddToDataDefinition(DEFINITIONS.StructString, "ATC GPS APPROACH TRANSITION ID", "number", SIMCONNECT_DATATYPE.STRING64, 0.0f, SimConnect.SIMCONNECT_UNUSED);
                my_simconnect.AddToDataDefinition(DEFINITIONS.StructString, "GPS WP NEXT ID", "number", SIMCONNECT_DATATYPE.STRING64, 0.0f, SimConnect.SIMCONNECT_UNUSED);
                my_simconnect.AddToDataDefinition(DEFINITIONS.StructString, "GPS WP PREV ID", "number", SIMCONNECT_DATATYPE.STRING64, 0.0f, SimConnect.SIMCONNECT_UNUSED);
                my_simconnect.AddToDataDefinition(DEFINITIONS.StructString, "HSI STATION IDENT", "number", SIMCONNECT_DATATYPE.STRING64, 0.0f, SimConnect.SIMCONNECT_UNUSED);
                my_simconnect.RegisterDataDefineStruct<StructString>(DEFINITIONS.StructString);

                my_simconnect.OnRecvSimobjectDataBytype += new SimConnect.RecvSimobjectDataBytypeEventHandler(simconnect_OnRecvSimobjectDataBytype);
            }

...
    
            try
            {
                my_simconnect.RequestDataOnSimObjectType(DATA_REQUESTS.REQUEST_1, DEFINITIONS.Struct1, 0, SIMCONNECT_SIMOBJECT_TYPE.USER);
                my_simconnect.RequestDataOnSimObjectType(DATA_REQUESTS.REQUEST_2, DEFINITIONS.StructString, 0, SIMCONNECT_SIMOBJECT_TYPE.USER);
            }
            catch (Exception eree)
            {
                MessageBox.Show(eree.Message, "YOU'RE FLOODING MSFS WITH REQUESTS!!! CHILL DUDE!", MessageBoxButtons.OK, MessageBoxIcon.Warning);
            }

...
    
        private enum DEFINITIONS
        {
            Struct1, StructString
        }

        private enum DATA_REQUESTS
        {
            REQUEST_1, REQUEST_2
        }
        
...
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    private struct StructString
        {
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
            public string ATCAIRLINE;
            public string ATCFLTNBR;
            public string ATCID;
            public string ATCMODEL;
            public string ATCTYPE;
            public string GPSAPPRAPRTID;
            public string GPSAPPRID;
            public string GPSAPPRTRANSID;
            public string GPSWPNEXTID;
            public string GPSWPPREVID;
            public string HSISTATION;
        }

...
    
        case DATA_REQUESTS.REQUEST_2:
            StructString structstr = (StructString)data.dwData[0];
            lblaATCAirline.Text = structstr.ATCAIRLINE; ;
            lblbATCFLTNBR.Text = structstr.ATCFLTNBR;
            lblcATCID.Text = structstr.ATCID;
            lbldATCMODEL.Text = structstr.ATCMODEL;
            lbleATCType.Text = structstr.ATCTYPE;
            lblfGPSAPPRAPRTID.Text = structstr.GPSAPPRAPRTID;
            lblgGPSAPPRID.Text = structstr.GPSAPPRID;
            lblhGPSAPPRTRANSID.Text = structstr.GPSAPPRTRANSID;
            lbliGPSWPNEXTID.Text = structstr.GPSWPNEXTID;
            lbljGPSWPPREVID.Text = structstr.GPSWPPREVID;
            lblkHSISTATIDENT.Text = structstr.HSISTATION;
            break;
 
From the FSX SDK:

When the units are listed as a structure or as a string, enter the empty string, or simply NULL, in the units parameter of this function call.

I would assume that still applies to the SimConnect for FS2020.
 
I was wondering about that myself since the SimWatcher program does not have the value of "String" as a unit value. Kewl! Thanks, Warpped!
 
From the FSX SDK:



I would assume that still applies to the SimConnect for FS2020.
I now know what I did wrong

I didn't know you had to put
Code:
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = XXX)]
before EACH and EVERY string variable. I thought putting this at the beginning of the struct would be sufficient... It wasnt xD
 
Thanks for reporting back on that tweak, Norbyx. I may have to use strings in the future.

Could I ask you to update your first post with the corrected code, with before and after fixes, please? I am not good at interpreting C# code... Or copy and paste into a new post with a sample fix.

Thanks!
 
Could I ask you to update your first post with the corrected code, with before and after fixes, please? I am not good at interpreting C# code... Or copy and paste into a new post with a sample fix.
I have put all the SimVar names in a separate class, so the main code does'nt look too full. Although you can also put the SimVar names in the main part of your code...

Class called "SimVarNames". Within that class I created some read only string arrays with the variable names. I have them in separate arrays so it's easier when it comes to the "AddToDataDefinition" part:
C#:
static public readonly string[] Miscstring64 = new string[]
{
    "ATC AIRLINE",
    "ATC ID",
    "ATC MODEL",
    "ATC TYPE",
    "GPS WP NEXT ID",
    "GPS WP PREV ID",
};

static public readonly string[] Miscstring8 = new string[]
{
    "HSI STATION IDENT",
    "ATC FLIGHT NUMBER",
};

static public readonly string[] Miscstring256 = new string[]
{
    "GPS APPROACH APPROACH ID",
    "GPS APPROACH TRANSITION ID",
    "Title",
    "GPS APPROACH AIRPORT ID",
};

In the main part of the code I have the "AddToDataDefinition" in a "foreach" loop, so i don't have to add new lines here every time I add some SimVars:
C#:
private void initDataRequest()
{
    try
    {
        my_simconnect.OnRecvOpen += new SimConnect.RecvOpenEventHandler(simconnect_OnRecvOpen);
        my_simconnect.OnRecvQuit += new SimConnect.RecvQuitEventHandler(simconnect_OnRecvQuit);
        my_simconnect.OnRecvException += new SimConnect.RecvExceptionEventHandler(simconnect_OnRecvException);
        
        ...
        
        foreach (string str in SimVarNames.Miscstring64)
        {
            my_simconnect.AddToDataDefinition(DEFINITIONS.StructMisc, str, "number", SIMCONNECT_DATATYPE.STRING64, 0.0f, SimConnect.SIMCONNECT_UNUSED);
        }
        foreach (string str in SimVarNames.Miscstring8)
        {
            my_simconnect.AddToDataDefinition(DEFINITIONS.StructMisc, str, "number", SIMCONNECT_DATATYPE.STRING8, 0.0f, SimConnect.SIMCONNECT_UNUSED);
        }
            my_simconnect.RegisterDataDefineStruct<Misc>(DEFINITIONS.StructMisc);
        foreach (string str in SimVarNames.Miscstring256)
        {
            my_simconnect.AddToDataDefinition(DEFINITIONS.StructMisc, str, "number", SIMCONNECT_DATATYPE.STRING256, 0.0f, SimConnect.SIMCONNECT_UNUSED);
        }
        my_simconnect.RegisterDataDefineStruct<Misc>(DEFINITIONS.StructMisc);
        
        ...
        
         my_simconnect.OnRecvSimobjectDataBytype += new SimConnect.RecvSimobjectDataBytypeEventHandler(simconnect_OnRecvSimobjectDataBytype);
         }
    catch (COMException op)
    {
        MessageBox.Show(op.Message, "ERR", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
}

The struct looks as folowes:
C#:
struct Misc
{
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
    public string ATCAIRLINE;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
    public string ATCID;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
    public string ATCMODEL;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
    public string ATCTYPE;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
    public string GPSWPNEXTID;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
    public string GPSWPPREVID;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)]
    public string HSISTATIONIDENT;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)]
    public string ATCFLIGHTNUMBER;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
    public string GPSAPPROACHAPPROACHID;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
    public string GPSAPPROACHTRANSITIONID;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
    public string Title;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
    public string GPSAPPROACHAIRPORTID;
}

Then at the end I have a switch case statement, so I can assign the data in the struct variables to a label:
C#:
 switch ((DATA_REQUESTS)data.dwRequestID)
 {
 
 ...
 
 case DATA_REQUESTS.RequestMisc:
     Misc misc = (Misc)data.dwData[0];
     lblaATCAirline.Text = misc.ATCAIRLINE;
     lblbATCFLTNBR.Text = misc.ATCFLIGHTNUMBER;
     lblcATCID.Text = misc.ATCID;
     lbldATCMODEL.Text = misc.ATCMODEL;
     lbleATCType.Text = misc.ATCTYPE;
     lblfGPSAPPRAPRTID.Text = misc.GPSAPPROACHAIRPORTID;
     lblgGPSAPPRID.Text = misc.GPSAPPROACHAPPROACHID;
     lblhGPSAPPRTRANSID.Text = misc.GPSAPPROACHTRANSITIONID;
     lbliGPSWPNEXTID.Text = misc.GPSWPNEXTID;
     lbljGPSWPPREVID.Text = misc.GPSWPPREVID;
     lblkHSISTATIDENT.Text = misc.HSISTATIONIDENT;
     lbllTITLE.Text = misc.Title;
 break;
        
...
    
 }

NOTE: If you have multiple data requests, at the beginning of each "case" statement you MUST add "STRUCT_NAME newVarName = (STRUCT_NAME)data.dwData[0]". "data" refers to the Variable name defined in the beginning of the void "simconnect_OnRecvSimobjectDataBytype". For example: "private void simconnect_OnRecvSimobjectDataBytype(SimConnect sender, SIMCONNECT_RECV_SIMOBJECT_DATA_BYTYPE data)"

Here are the Enums for the requests:
C#:
private enum DEFINITIONS
{
    Struct1, StructString,
    StructAutopilot,
    StructGear,
    StructSpeed,
    StructAltitudes,
    StructEngines,
    StructGPS,
    [B]StructMisc,[/B]
    StructPlane,
    StructWarnings,
}

private enum DATA_REQUESTS
{
    REQUEST_1, REQUEST_2,
    RequestAutopilot,
    RequestGear,
    RequestSpeed,
    RequestAltitudes,
    RequestEngines,
    RequestGPS,
    [B]RequestMisc,[/B]
    RequestPlane,
    RequestWarnings,
}

And finally the "RequestDataOnSimObjectType" part:
C#:
 my_simconnect.RequestDataOnSimObjectType(DATA_REQUESTS.RequestMisc, DEFINITIONS.StructMisc, 0, SIMCONNECT_SIMOBJECT_TYPE.USER);

I have this inside of a timer so the data is always up to date without flooding MSFS with data requests xD

If I have forgotten something or you have questions about the code, feel free to ask!! :D
 
I have this inside of a timer so the data is always up to date without flooding MSFS with data requests xD
Or you could tell SimConnect to only send you the data values when they've changed. Saves even more "bandwidth".
 
Top