Can't get READ SimVar CallBack to work properly using C++ x64 WinForm in VS2022

I’m trying to read engine RPM in a Cassna 152. After trying a multitude of
ways to read a SimVar, I ended with Lockheed’s sample and tweaked it (like
changing the read interval to Frames). { I isolated my SimConnect code to
separate .h file cause I use a USB code generator for my PIC and C++ code,
and it rewrites over the C++ program. That way I have very little
modifications to re-do each time I change my USB code. }
I trigger the
CallBack from a Button control for the time being, it will be moved into a
Timer later.

private: System::Void MainForm_Load(System::Object^ sender, System::EventArgs^ e)
{
    OpenSimConnect();
}

private: System::Void bUpdateIndicators_Click(System::Object^ sender, System::EventArgs^ e)
{
    SimConnect_CallDispatch(hSimConnect, MyDispatchProcPDR, NULL);

//this->tbEngine1_RPM->Text = intEngine1_RPM;
}

private: System::Void bClose_Click(System::Object^ sender, System::EventArgs^ e)
{
    CloseSimConnect();
    Application::Exit();
}

The SimConnect code:

//Copyright (c) Lockheed Martin Corporation.  All rights reserved. 
//------------------------------------------------------------------------------
//
//  SimConnect Tagged Data Request Sample
// 
//    Description:
//                After a flight has loaded, request the vertical speed and pitot
//                heat switch setting of the user aircraft, but only when the data
//                has changed
//------------------------------------------------------------------------------
#pragma once

#include 
#include "SimConnect.h"
#include 
#include 
#include 

int     quit = 0;
HANDLE  hSimConnect = NULL;

// A basic structure for a single item of returned data
struct StructOneDatum {
    int        id;
    float    value;
};

// maxReturnedItems is 3 in this case, as the sample only requests RPM,
// vertical speed and pitot heat switch data
#define maxReturnedItems    3

// A structure that can be used to receive Tagged data
struct StructDatum {
    StructOneDatum  datum[maxReturnedItems];
};

static enum EVENT_PDR {
    EVENT_SIM_START,
};

static enum DATA_DEFINE_ID {
    DEFINITION_PDR,
};

static enum DATA_REQUEST_ID {
    REQUEST_PDR,
};

static enum DATA_NAMES {
    DATA_ENGINE1_RPM,
    DATA_VERTICAL_SPEED,
    DATA_PITOT_HEAT,
};

void OpenSimConnect()
{
    HRESULT hr;

    if (SUCCEEDED(SimConnect_Open(&hSimConnect;, "Open", NULL, 0, 0, 0)))
    {
        hr = SimConnect_AddToDataDefinition(hSimConnect, DEFINITION_PDR, "PROP RPM:1", "rpm",
            SIMCONNECT_DATATYPE_FLOAT32, 0, DATA_ENGINE1_RPM);

        hr = SimConnect_AddToDataDefinition(hSimConnect, DEFINITION_PDR, "Vertical Speed", "Feet per second",
            SIMCONNECT_DATATYPE_FLOAT32, 0, DATA_VERTICAL_SPEED);

        hr = SimConnect_AddToDataDefinition(hSimConnect, DEFINITION_PDR, "Pitot Heat", "Bool",
            SIMCONNECT_DATATYPE_FLOAT32, 0, DATA_PITOT_HEAT);

        hr = SimConnect_RequestDataOnSimObject(hSimConnect, REQUEST_PDR, DEFINITION_PDR,
            SIMCONNECT_OBJECT_ID_USER, **SIMCONNECT_PERIOD_VISUAL_FRAME** , 0, 0);
    }
}

void CloseSimConnect()
{
    HRESULT hr;
    hr = SimConnect_Close(hSimConnect);
}

void CALLBACK MyDispatchProcPDR(SIMCONNECT_RECV* pData, DWORD cbData, void* pContext)
{
    HRESULT hr;

    float fltEngine1_RPM;

    switch (pData->dwID)
    {
        case SIMCONNECT_RECV_ID_SIMOBJECT_DATA:
        {
            SIMCONNECT_RECV_SIMOBJECT_DATA* pObjData = (SIMCONNECT_RECV_SIMOBJECT_DATA*)pData;

            switch (pObjData->dwRequestID)
            {
                case REQUEST_PDR:
                {
                    int    count = 0;;
                    StructDatum* pS = (StructDatum*)&pObjData-;>dwData;

            // There can be a minimum of 1 and a maximum of maxReturnedItems
            // in the StructDatum structure. The actual number returned will
            // be held in the dwDefineCount parameter.

                    while (count < (int)pObjData->dwDefineCount)
                    {
                        switch (pS->datum[count].id)
                        {
                            case DATA_ENGINE1_RPM:
                                // printf("\nEngine 1 RPM = %f", pS->datum[count].value);
                                fltEngine1_RPM = pS->datum[count].value;
                                break;

                            case DATA_VERTICAL_SPEED:
                                // printf("\nVertical speed = %f", pS->datum[count].value);
                                break;

                            case DATA_PITOT_HEAT:
                                // printf("\nPitot heat = %f", pS->datum[count].value);
                                break;

                            default:
                                // printf("\nUnknown datum ID: %d", pS->datum[count].id);
                                break;
                        }
                        ++count;
                    }
                    break;
                }
                default:
                break;
            }
            break;
        }
    }
}

I added Engine1 RPM for my application. I get weird data when I add a
Breakpoint at where “I think” Engine 1 RPM is read. I’m a main-frame
programmer by trade, so please disregard my ignorance when it comes to coding
C++ and using structures and variables properly in C++ (I only started C++
this year on my own). My goal is to get that Engine 1 RPM into a variable
that I can handle, like displaying on-screen, but the main goal is to move to
USB variables for download to a PIC mcu.
Thanks for any help! Robert

Hi @Robert_H, It looks like you are trying to request data on a SimObject
through tagged values. In that case, your call to
SimConnect_RequestDataOnSimObject should read:

hr = SimConnect_RequestDataOnSimObject(hSimConnect, REQUEST_PDR, DEFINITION_PDR,
            SIMCONNECT_OBJECT_ID_USER, SIMCONNECT_PERIOD_VISUAL_FRAME, SIMCONNECT_DATA_REQUEST_FLAG_TAGGED, 0);

Please have a look at the online documentation for more information:
https://docs.flightsimulator.com/html/Programming_Tools/SimConnect/API_Reference/Events_And_Data/SimConnect_RequestDataOnSimObject.htm
Also note that the SDK comes with SimConnect samples - there’s one called
“TaggedData” that might be helpful in your case. Best regards, Eric / Asobo

Yeah, I added VISUAL_Frame last night before going to bed. At least now it
loops through automatically without me bashing on a button. I saw that
Tagged_Data sample after I found that code; it’s essentially the same as
what I found from Lockheed. But my quest remains; how do I get to that
engine RPM value hidden in the structure?
How can I move it to ordinary
variables so I can format them for display and transfer by USB as desired?
I
don’t know enough C++ to copy it to a variable I can use in a WinForm, or to a
variable for a USB interface (I use HIDmaker FS2). I just started earlier this
year, I was a COBOL programmer that dabbled in VB6. I can’t just display the
contents on a textbox, it generates error C2665 and E1767.

this->tbRPM->Text = (Struct1*)&pObjData-;>dwData;

This statement seems so simple at first glance to you, but I’m using a
WinForm , I can’t just Printf.

printf("\nEngine 1 RPM = %f", pS->datum[count].value)

But to me, I can’t figure out how to get to “value”. I tried all sorts of
conversion routines.

sprintf(chrEngine1_RPM, "%f", pS->datum[count].value);

Robert “Noob lost in the woods” PS: I asked on C++ forums, but the only
reply I got was essentially, " easy, just convert it with Sprintf ".

@Robert_H, You wrote above “I added VISUAL_Frame last night” but it could
already be seen in your previous message - what I told you was missing was
“SIMCONNECT_DATA_REQUEST_FLAG_TAGGED” - is it what you meant to write? As for
your question regarding how to use WinForms I don’t think this belongs to the
DevSupport platform - we provide support for the MSFS SDK/DevMode but the
question seems related to a specific tech that is outside of this scope. Maybe
someone from the community who uses WinForms will be able to help you. Best
regards, Eric / Asobo

Yup, I’ve added the parameters as in the Tagged SDK example. I’ve since
learned that C++ .Net programs are managed code, while the CallBack routine is
unmanaged code. I can’t be the only guy on this forum that is "looking for a
way to get the variable value that he just read on SimConnect
back to the
managed code"
side (in my case it’s mainly an interface to USB, and out to my
instrument panel. So far the " C++ experts on google " are baffled by the
SimConnect code, none I’ve seen seem to be able to figure out the structures.
But it’s certain that those that do know how to get to the values are here.
Robert

can’t just display the contents on a textbox, it generates error C2665 and
E1767. Because you can‘t print an entire struct or assign it to a
(presumed) text widget. You need to cast the „raw bytestream data“ to the
expected struct first, and then access its individual (and now properly typed)
struct members. Basic C++ struct knowledge :wink: And you probably still need to
explicitly format the received integer to a („printable“) text. Sorry, C++
ain‘t JavaScript :wink: They say that „in C++ you don‘t pay for what you don‘t
need“. But the opposite holds as well: „You pay dearly (in terms of coding
efforts, that is) for what you need“ Well, mostly anyways. There are
frameworks in C++, too, that do „the dirty work for you“ (I use Qt). Oh, and
if you thought that „casting the received bytestream“ was enough, think again:
make double-sure to enforce „tight packing“ of your struct definition!
Otherwise your compiler will likely do its duty and optimise the struct member
access for performance, that is e.g. align the members to 64bit address
boundaries. Not a big deal if you have a bunch of FLOAT64 (only), but results
will vary between „hmmm, that‘s interesting…“ and „bloody %<#+! compiler,
WTF!“ if you add FLOAT/INT32 to the mix :wink:

Here are some real-world examples, in C++:
https://github.com/till213/SkyDolly/blob/main/src/Plugins/Connect/MSFSSimConnectPlugin/src/MSFSSimConnectPlugin.cpp
And an example struct:
https://github.com/till213/SkyDolly/blob/main/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/Position/SimConnectPositionCommon.h
Again, pay extra attention to the #pragma compiler directive: it tells the
compiler to „tightly pack the data“, because that is how we receive it from
MSFS (and how MSFS expects the „byte stream“). Btw that „packing“ is not
specific to C++, you e.g. need to tell the same a C# compiler. And believe me,
I am talking from (bloody) experience :wink:

Thank you for that detailed explanation, it confirms what I thought I
understood. I read about " packing ", " enforcing " and " wait till the
garbage man comes by and craps all over your stuff
" in my searches. :smiley: I
have one last test to complete, then I’ll report back with what I found before
closing this thread (please). Robert :slight_smile:

I just wanted to confirm that I can have multiple SimVars using this technique
(I was only using Prop RPM for my tests). I now read 7 SimVars on each loop
and I’ve bumped up my SimConnect delay to 10ms and I’m still a tad faster
under full throttle acceleration than SimWatcher with full slider (I left only
3 variables).

No Callback , no
tagged logic , just basic SimConnect all done in MainForm. Robert :slight_smile: