1. This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn More.

using SimConnect_GetNextDispatch() to get received data

Discussion in 'SimConnect' started by Enrack, 19 May 2017 at 17:10.

  1. Enrack

    Enrack

    Joined:
    21 Apr 2017
    Messages:
    5
    Country:
    france
    Hi,
    (Sorry for my english in advance)
    I have a little issue with retrieving some FSX variables.
    I have a c++ project where I would like to retrieve some variables accorded to the user's aircraft currently playing, such as the speed/acceleration - longitude/latitude/altitude - pitch/roll/heading.

    According to this very complete source : https://msdn.microsoft.com/en-us/library/cc526983.aspx ,
    I had first, to use the funtion -SimConnect_AddToDataDefinition() in order to "build" a data group definition to be able, to successfuly use the SimConnect_RequestDataOnSimObject() funtion in order to get the wanted variables accessible in my project.
    But after this last funtion, it appears (everywhere), that the data requested are present in the c++ project but are still in a "cache zone" which I have to take in charge.

    I have readen that I need to use one of these 2 groups of funtion, in order to get those datas which are still inavailable because of the "cache zone" :

    -SimConnect_GetNextDispatch()

    OR

    -SimConnect_CallDispatch()
    AND
    -DispatchProc()

    So, I was going for the first funtion (SimConnect_GetNextDispatch()) because I believe that it may work.
    The thing is, I really don't understand how it works :

    This is the syntaxe (more like the declaring prototype) of the SimConnect_GetNextDispatch() funtion :

    HRESULT SimConnect_GetNextDispatch(
    HANDLE hSimConnect,
    SIMCONNECT_RECV** ppData,
    DWORD* pcbData
    );

    So it appears that we have to give the funtion a ppData which is SIMCONNECT_RECV** type.
    What is this ppData in this case ? Is it the actual structure which is still in the inaccessible "Cache zone" ? Or is it a new SIMCONNECT_RECV** type that we must create just before calling the funtion ?
    Because in both cases, I don't understand how to process.

    I though I had to create (declare) my own SIMCONNECT_RECV** ppData2 for example, and then give it to the SimConnect_GetNextDispatch() funtion ; But then, looking for informations about this ppData type, I came to this :

    SIMCONNECT_RECV_SIMOBJECT_DATA

    "The SIMCONNECT_RECV_SIMOBJECT_DATA structure will be received by the client after a successful call to SimConnect_RequestDataOnSimObject or SimConnect_RequestDataOnSimObjectType."

    So, where will it be received ? How can we get it ? Do we have to get it, or create our own and then use an other funtion to fil it with the real actual "received" structure ?

    This is the head prototype of the structure :

    struct SIMCONNECT_RECV_SIMOBJECT_DATA : public SIMCONNECT_RECV {
    DWORD dwRequestID;
    DWORD dwObjectID;
    DWORD dwDefineID;
    DWORD dwFlags;
    DWORD dwentrynumber;
    DWORD dwoutof;
    DWORD dwDefineCount;
    DWORD dwData;
    };

    And "the dwData[0] member will contain your struct as System.Object. This should be cast to the proper type."
    What does that mean ?
    I was looking for an example of using, and I came to that :
    ------------------------------
    hr = SimConnect_RequestDataOnSimObject(hSimConnect, REQUEST_1, DEFINITION_1, SIMCONNECT_OBJECT_ID_USER, SIMCONNECT_PERIOD_ONCE);

    ....
    // When the data is received - cast it to the correct structure type

    case SIMCONNECT_RECV_ID_SIMOBJECT_DATA:
    {
    SIMCONNECT_RECV_SIMOBJECT_DATA *pObjData = (SIMCONNECT_RECV_SIMOBJECT_DATA*)pData;

    switch(pObjData->dwRequestID)
    {
    case REQUEST_1:

    Struct1 *pS = (Struct1*)&pObjData->dwData;

    // Add code to process the structure appropriately

    break;
    }
    break;
    }
    ------------------------------
    And this is the Struct1 definition :

    struct Struct1
    {
    double kohlsmann;
    double altitude;
    double latitude;
    double longitude;
    };
    ------------------------------

    So, what are *pobjData and pData ? Are those created variables which where created/declared at the same line they are used ?
    What is Struct1 in this case ? Is it the created "empty/not felt" structure which will receive the actual dwData we are looking for ?
    And why is there even a "case" without any switch before ?
    I don't even understand why is there a :

    static enum DATA_DEFINE_ID {
    DEFINITION_1,
    DEFINITION_2
    };

    to use with the AddToDataDefinition() like that : hr = SimConnect_AddToDataDefinition(hSimConnect, DEFINITION_1, "Kohlsman setting hg", "inHg");
    When this funtion actually needs a "SIMCONNECT_DATA_DEFINITION_ID" type for the def_ID, which isn't the same thing as DATA_DEFINE_ID.

    I'm getting really lost now. This was all really clear at the beining, but now, there is some lack of detailled information in this doc, and it makes it really hard for me, as I don't have the reals big definitions of each funtions (and that's normal, I don't disagree that).

    So if you can give me some answer or some other webSite/documentation to share me with more or differents details, I'll be glad to take them all.

    Thanks in advance for your answers.

    Regards,

    bas-tiras@hotmail.fr
     
  2. kabekew

    kabekew

    Joined:
    16 Mar 2010
    Messages:
    51
    Country:
    unitedstates
    SimConnect is complicated C code in the style used in the 1990's, but I'll try to explain and I hope it helps you and maybe others a little.

    Remember SimConnect is all message-based. You send messages to FSX, like a request for data, through one of the SimConnect functions (which forwards it on), and some time later FSX sends a reply back to SimConnect. Or you send a "subscribe to event" message, and FSX sends you the appropriate message whenever that event occurs.

    They provide two ways to retrieve those messages, depending if you're a DLL addon or a separate EXE. Either one will work with an EXE, but with a DLL, unless you're doing fancy things with other threads, you need to use the second method (SimConnect calling your callback function whenever you get a message).

    With either method, the "pData" or "ppData" is the actual message FSX is sending to you. (I assume you are comfortable with pointers in C, and the standard naming convention where "p" means "pointer to," and "pp" means "pointer to a pointer to"). The block of memory containing the message is "owned" by SimConnect -- it gives you a pointer to it, and the way it does that is you give it a pointer to your pointer variable, which it sets to point to the message. That's how you return multiple things in C. So to answer your next question:

    The first parameter in the SimConnect_GetNextDispatch -- HANDLE hSimConnect -- is something you give to the function, and the other two are pointers to variables that you own, which the function will fill in to point to the returned data (which it owns, so don't change it or try to delete it). The only way you can know what you are giving the function and what it is giving you is from documentation (in this case: https://msdn.microsoft.com/en-us/library/cc526983.aspx#SimConnect_GetNextDispatch ).

    So according to that, you pass it the relevant hSimConnect, a pointer to your variable which is a pointer to SIMCONNECT_RECV structure, and a pointer to another of your variables which is a DWORD. It will fill in the last two to give you a pointer to the message and its size. So as in the documentation example:

    Code:
    SIMCONNECT_RECV* pData;   
    DWORD cbData;
    
    hr = SimConnect_GetNextDispatch(hSimConnect, &pData, &cbData);
    
    If the function succeeded, it means you have a new message, and your variable pData points to the message sent by FSX (in a block of memory owned by SimConnect).

    Each FSX message is a structure, and all derive from the structure SIMCONNECT_RECV. I assume you understand how structures can derive from others (child structure and parent structure)? So in memory, if you have:

    Code:
    struct Header
    {
         long MessageType;
         long OtherHeaderData;
    }
    struct Message1 : public Header  
    {
         char Message1Data;
         int   Message1MoreData;
    }
    struct Message2 : public Header
    {
         DWORD Message2Data;
         unsigned int Message2MoreData;
         long Message2EvenMoreData;
    }
    
    ...in memory, an instance of Message1 will look like this:

    Code:
    long MessageType;
    long OtherHeaderData;
    char Message1Data;
    int   Message1MoreData;
    
    And message2 like this:

    Code:
    long MessageType;
    long OtherHeaderData;
    DWORD Message2Data;
    unsigned int Message2MoreData;
    long Message2EvenMoreData;
    
    So to pass different messages of different sizes that all derive from "struct Header", FSX will just pass a pointer to the Header, and one of the Header elements (here "MessageType") is set to a value known by both caller and receiver so the receiver knows what the "real" structure is and can re-cast the pointer to point to that actual structure.

    Going back to where you receive the message:

    Code:
    SIMCONNECT_RECV* pData;   
    DWORD cbData;
    
    hr = SimConnect_GetNextDispatch(hSimConnect, &pData, &cbData);
    
    SIMCONNECT_RECV is the "header" structure. Following the documentation example, you then look at it's "MessageType" variable to create a pointer to its actual struct:

    Code:
    switch(pData->dwID)
    { 
         ....
         case SIMCONNECT_RECV_ID_SIMOBJECT_DATA:   //header says I'm actually this kind of struct
               SIMCONNECT_RECV_SIMOBJECT_DATA *pObjData = (SIMCONNECT_RECV_SIMOBJECT_DATA*) pData; //create a variable that points to the same thing, but knows it's a SIMCONNECT_RECV_ID_SIMOBJECT_DATA struct, not just a header
    
    }
    
    Now you have a pointer pObjData that points to the full structure SIMCONNECT_RECV_ID_SIMOBJECT_DATA.

    As you say, that structure is defined like this:

    Code:
    struct SIMCONNECT_RECV_SIMOBJECT_DATA : public SIMCONNECT_RECV {
      DWORD  dwRequestID;
      DWORD  dwObjectID;
      DWORD  dwDefineID;
      DWORD  dwFlags;
      DWORD  dwentrynumber;
      DWORD  dwoutof;
      DWORD  dwDefineCount;
      DWORD  dwData;
    };
    
    Now your question:

    Here they do something tricky, but it saves processing time and memory. This message of course comes from FSX when you request a set of data that is defined by you. When FSX was compiled, it doesn't know the size of the data a user may request. So in that structure above (SIMCONNECT_RECV_SIMOBJECT_DATA), they declare the returned data is only a DWORD (dwData). When FSX is running and actually sends that structure, it puts the first 4 bytes of your defined structure in dwData, then adds the rest to the memory right after that. So you are reading "past" the returned structure, which can be confusing. Think of it more like receiving a series of data in a block of memory, not a "structure object".

    That's why in your example they do this:

    Code:
    
    I was looking for an example of using, and I came to that :
    ------------------------------
    hr = SimConnect_RequestDataOnSimObject(hSimConnect, REQUEST_1, DEFINITION_1, SIMCONNECT_OBJECT_ID_USER, SIMCONNECT_PERIOD_ONCE);
    
    ....
    // When the data is received - cast it to the correct structure type
    
    case SIMCONNECT_RECV_ID_SIMOBJECT_DATA:
    {
      SIMCONNECT_RECV_SIMOBJECT_DATA *pObjData = (SIMCONNECT_RECV_SIMOBJECT_DATA*)pData;
    
      switch(pObjData->dwRequestID)
      {
        case REQUEST_1:
    
          Struct1 *pS = (Struct1*)&pObjData->dwData;
    
    Here, the structure you defined (Struct1) that you want FSX to fill when you request data, is put into the first four bytes of dwData, then the rest of it follows in memory. So:

    Code:
      Struct1 *pS = (Struct1*)&pObjData->dwData;
    
    ... creates a variable pS that points to the structure "Struct1" that starts at the location of dwData in memory (remember Simconnect adds the rest of your requested structure to the end of SIMCONNECT_RECV_SIMOBJECT_DATA). That is now a pointer to the returned data that you requested.

    I hope this helps you understand it a little better.
     

Share This Page