Using the SimConnect SDK - A SimVar Request handler (and potential SDK replacement)

Hi FARyoufly,

I’ve created a new branch for you to pull down.

The only real change I’ve made is to ensure the SimConnectHelper project is using the .NET Core 3.0 Framework.
SimConnectHelper Target Framework

Please let me know if this cures your problem.

Hello @Dragonlaird,

This is a GREAT post! I just started to learn all about SimConnect, and this post is really helpful. I have experience with C# in Visual Studio, and already experimented by building my own tools to connect my custom controls based on USB-connected PIC-Microcontrollers with a 3rd party A320 add-on for FSX (Jeehell FMGS - very realistic A320 simulation). I was using their proprietary API for that - nothing to do with SimConnect.

But FSX is getting old, and I want to move to FS2020. Unfortunately, Jeehell doesn’t have a version of their add-on in FS2020, and it is very unlikely to have something available soon. So if I want to move to FS2020, I have to look for a realistic A320 add-on.

I’m very interested in the FlyByWire A32NX project. I read from some explanation on their website that SimConnect obviously doesn’t cover all the complexities of custom add-ons. I read on their website: “To make it possible for 3rd party aircraft developers to go beyond these limitations, Microsoft Flight Simulator enables aircraft developers to create their own API, most commonly in the form of so called sim-vars and sim-events.” On this page on their website they call it “Custom LVARs” and “Custom Events”.

Could you give some explanation about this? I assume that your SimConnectHelper can also handle those? As an example, an extract from their documentation below shows the definition of the BAT 1 and BAT 2 switches on the A320 overhead panel. Could you provide some example code how to read and/or write to this?

1 Like

Hi @HBilliet ,

Unfortunately, I’m not very familiar with LVARS etc, as I’ve not had time to experiment with that code yet, however, there are some great posts in this forum discussing them and how to implement them.

I would suggest posting your request in a new thread, where others will undoubtedly provide links and suggestions for you.

The aim of this post was to help people get started on using the SDK, to provide simplified a C# project that could be ported to other platforms and wrap the clunky/quirky SDK using a more modern methodology to improve useability, it needs some more work to refine the code, as there are a couple of minor improvements needed.

As I’m currently working on another project (unrelated), I’ve not got enough time to go back to this (yet). I will revive this and add to the existing code once this current project is complete.

Hope this helps.

1 Like

Hello @Dragonlaird ,

First of all, I really appreciate all the efforts you put in this post, explaining all in full detail. I’m sure that this is not only helpful for me, but for a lot of people. Thanks a million!!!

I could build and run the “SimConnect Test”. It connects with the sim, and I can read variables. This is at least a very good WORKING starting point to learn the details, supported with all the documentation you provided.

But I tried it with FBW A32NX, and I’m not sure if I’m doing things right. In some other post, I read that I should use the variable “A:AUTOPILOT ALTITUDE LOCK VAR:3” to read the current value of the altitude, but I see an error in your logging window telling that this variable is not recognized:

image

But I can find this variable in the Behavior window, and I also found it on HubHop (A:AUTOPILOT ALTITUDE LOCK VAR:3, Feet) (mobiflight.com)

But the unit should be “feet”, and your tool says “bool”? Maybe that is the issue?

One other more common question:

Looking at the Behavior window, the variables have a prefix “A:”, “L:”, “H:”, etc… The SimConnect SDK documentation says:

When using RPN to create expressions, you can use different variables, and these will require a prefix so the simulation can correctly identify their type and where they come from.

I assume that all variables with prefix “A:” are within the scope of your tool, right? Because these are (according to the SDK Documentation) the so called “SimVars”.

But what with other types? I know that L-vars and H-vars should be controlled through WASM (Paul Henty wrote a very nice C# tool for this: Added Support for LVars/HVars Access in MSFS via John Dowson’s WASM Module. - FSUIPC Client DLL for .NET - The simFlight Network Forums).

But if I, for example, want to deal with the below.

image

Next to some SimVars (type A), I also need to control some K-var (>K:TOGGLE_EXTERNAL_POWER). Can this also be done by your tool? Or does every type of variable require a seperate tool? I hope not, because if I read the SDK Documentation, we almost have the whole alphabet :slight_smile: That’s a lot of tools…

EDIT:

I made a mistake, the variable I should read is “AUTOPILOT ALTITUDE LOCK VAR:3” - see portion of Behavior window below:
image

When I enter this in your tool, it correctly shows “feet” now. But I still get errors, and the value is not correctly updating.
image

It even gets more weird, because sometimes I only get the error when deleting the variable from the list. And depending on the “Freq:”, I also might get different results. Sometimes it shows 0, sometimes a pretty random value like 2.75468616, and sometimes even a near 0 value with …E-356 (or some negative value like this - I can’t regenerate so can’t give exact values).

TIP: It would be a nice feature to be able to delete the contents of the logging screen.

Hello @Dragonlaird,

(I deleted my previous post, because I was wrongly referring to L-Vars)

But good news is that I found the issue - or the 2 issues!

1) UnitType = null

I now realize that several variables in the file SimVarUnits.cs have the value “null” for UnitType. Probably because you didn’t find the time to check all the types?

As most SimVars seem to be 64-bit floats (I got some help on the Discord-server of FBW), I have changed it to “float64”.

{"AUTOPILOT ALTITUDE LOCK VAR",new SimVarDefinition("AUTOPILOT ALTITUDE LOCK VAR","Selected altitude","feet",ConvertType("float64"),false,true)},

Now I don’t get the “UNRECOGNIZED_ID” exception, so I guess that this was the root cause of it.

2) Index seems not to work

If I use the index 3, or just no index, I get the same value.

image

image

The value that I’m getting is the “Selected Altitude”, which is the one you “give” to the flightcomputer by pushing the ALT knob. This is not the dialed value, which is changing when you turn the knob left or right. Index 3 should give me the dialed value, but that seems not to be working. The proof is that if I use the “MSFS SDK/Sampes/SimvarWatcher”, the index 3 value is showing the dialed value in real time.

Can you have a look at this?

EDIT:

I think I solved it myself in your sourcecode.

It seems that your index gets lost somewhere. If I debug in the file “SimConnectHelper.cs” (yep, the core of your tool :sweat:), and look in the call “RegisterSimVar”, the parameter simVarName in the call AddToDataDefinition is missing the index. If I set a breakpoint, and evaluate simVarname, it shows “AUTOPILOT ALTITUDE LOCK VAR” in stead of “AUTOPILOT ALTITUDE LOCK VAR:3”.

And the cause of this is just before the call to AddToDataDefinition, where you indeed strip the index from the simVarName.

if (simVarName.IndexOf(':') > -1)
{
  index = double.Parse(simVarName.Substring(simVarName.IndexOf(':') + 1));
  simVarName = simVarName.Substring(0, simVarName.IndexOf(':'));
}

simConnect.AddToDataDefinition(simReq.DefID, simVarName, unit, simReq.SimType, 0.0f, SimConnect.SIMCONNECT_UNUSED);

Why are you doing that? The “index” variable is even never used. So SimConnect can not know that I want “index :3” of my variable.

I did a simple test by forcing simVarName just before the call to AddToDataDefinition, and BINGO!

simVarName = "AUTOPILOT ALTITUDE LOCK VAR:3";
simConnect.AddToDataDefinition(simReq.DefID, simVarName, unit, simReq.SimType, 0.0f, SimConnect.SIMCONNECT_UNUSED);

Now I’m showing the dialed value instead of the selected speed.

Conclusion: I removed the “stripping the index code”

And now it works as a charm:
image

I can show both the dialed value (19100), and the one that was previously selected by pushing the ALT knob (17600).

Hi Hans,

To answer some of your points (thanks for making them and also for diving into the code to investigate).

  1. Some of the units are missing

Sadly, yes they are. At the time of creating the initial code, I took all of the SimVars from the documentation and put everything into a spreadsheet, which I then used a formula to build the C# output. But many of the SimVar definitions were incomplete (some were even wrong), with no units defined etc.

  1. Index values get lost

This isn’t much of a surprise. The original code was developed to help people understand how to use the SimConnect.dll and provide a guide with examples on how to use it.

From there, it became a wrapper to avoid requiring things like window handles and the callback method the SimConnect.dll uses. Many of the more advanced options available via SimConnect haven’t been added yet (but as you’ve seen, the code is flexible to allow additions).

I started writing code to handle indexing but it wasn’t completed before I had to pause working on it and move on to a different project. It also doesn’t yet handle values such as BCD and many other gaps, but it could with a little more work.

Perhaps you would like access to the repo, to be able to create branches and edit the code? You could then submit your own code changes to continue the development in my absence?

Jim

Hello Dragonlaird,

I’m happy to read that all makes sense now :slight_smile:

As of your question if I would create branches. I have no account on GitHub, and am not familiar with it neither. My intention was to use your code as a learning experience, and even build my own SimConnect module. Reason is that I have some very specific needs to connect with my PIC-microcontroller based hardware (not Arduino, hence a lot of tools out there are not usable).

On top of that I would like to add a connection to WASM as well (I like some challenges :slight_smile:). If not, I need to use 2 tools, which is yours and another one written by Paul Henty ( Added Support for LVars/HVars Access in MSFS via John Dowson’s WASM Module. - FSUIPC Client DLL for .NET - The simFlight Network Forums). But the DLL of Paul Henty is closed source, so I will need to build something myself (although, lot’s of examples out there, and I did already some first babysteps which seems to react with MSFS2020).

I’m even not sure how the “remote capabilities” that you built in your tool will work together with the WASM module. In a previous tool that I developed to work with a specific FSX add-on (Jeehell A320 FMGS), I built my own socket based connections so that I could distribute my hardware over different PC’s. I might re-use that code and combine it with yours. The connection with FSX was built in a DLL, which was then communicating via sockets to other “hubs”, which then connected to my PIC-based hardware using USB (even made it “hot-pluggeable”).

So I’m afraid that I’m using your code only as a very in-depth study object, because it’s a good WORKING starting point. I don’t have that much experience as you have after all :slight_smile: But I will definitly share my results in this forum, and you will be one of my main mentions!

Hello @Dragonlaird,

I hope you don’t mind if I ask some questions about your source code?

I have been able to start from a fresh VS2019 project, and created my own SimConnectDLL (Class Library using the .Net Framework) and a SimConnectTest (Windows Forms App using the .Net Framework). I could strip your SimConnectHelper code to the bare necessities to connect/disconnect with SimConnect and setting the neccessary EventHandlers. Next I analysed this piece of code in full detail - I just want to learn and understand.

If I’m correct, the static “Connect()” method is doing the following (and yes, I removed the option to connect over the network, as explained in my previous thread):

  1. Start a “messagePump” Task (call it a seperate thread) and use an AutoResetEvent to be signaled back if the messagePump is running before proceding.
  2. The “messagePump” Task does a few things:
    2.a) Create the messageHandler, which is derived from NativeWindow, which does nothing more than running it’s own WndProc catching WM_USER_SIMCONNECT and sending this to its own EventHandler “MessageReceived” (this eventhandler will be assigned later in ConnectFS)
    2.b) Call ConnectFS which is connecting with SimConnect and doing all the initializations of the neccessary EventHandlers.
    2.c) Signals back to the main thread that the messagePump is running, and that it can proceed

The “Disconnect()” stops the messagePump (via StopMessagePump()), signals SimConnect that the client is stopped, and does some cleanup. StopMessagePump is stopping the NativeWindow based window, and is also disposing the AutoResetEvent used for the signaling.

A few questions:

  1. Why do you use the CancellationTokenSource? I could run the code without it. But is that needed to make sure that the Task is also removed after it’s not needed any longer? With other words, if I don’t use CancellationTokenSource, it keeps running for ever, hence creating memory leaks (and probably performance degradations) if I Connect/Disconnect many times?
    I’m asking it, because I did some debugging, and in my opinion, this cancellation stuff is even not working, because every time I connect/disconnect, a new task is started without destroying the old one. Below screenshot is after 3 connect/disconnect cycles:


    I posted a question on stackoverflow as well c# - How to cancel a task - trying to use a CancellationTokenSource, but I think I don’t get the concept - Stack Overflow

  2. Why is the method MessageReceived still checking if “msg.Msg == WM_USER_SIMCONNECT”? Isn’t the MessageHandler not only calling it when “msg.Msg == WM_USER_SIMCONNECT”, so this second check is redundant?

Hi Hans,

Point 1.

When starting a new Thread, you should always include code within that thread to recognise when the “parent” is shutting down so that it can also stop cleanly and clean up after itself.

If your application is multi-threaded (several threads launched by the “parent”), then the easiest way to achieve this is to launch each thread and pass it a CancellationToken. It’s perfectly normal to pass the same token to each thread for this purpose (you only need multiple tokens if you plan to launch/stop different threads independently of each other).

This then allows the parent to cancel that token, indicating to the thread that it must stop.

In the parent, you will likely have a method that is called to stop your application, this may even be the Dispose() method itself. This is where you would cancel the token to force any child threads to also stop and dispose of any used variables.

Using tokens has an added feature in that you can tell the token to force an exception if the token is cancelled. This may seem harsh, after all, we would never normally want to deliberately cause an exception in our own code right?

Wrong. It is quite common to raise exceptions in your own code if you know those exceptions are handled correctly.

The advantage of raising an exception in this manner is, we don’t need to keep writing lots of code throughout our thread to check if the token has been canceled. By raising an exception, the token will force our thread to stop no matter what it was doing at the time. Since it is our own managed code, stopping the thread will then force the cleanup process to remove it from memory.

In this situation, I may not have completed writing all the code within the messagePump to check for the token being canceled (although as mentioned, this isn’t always required for raised exceptions) or maybe I forgot to catch the exception and unload the task handler cleanly. If that is the case, I apologise, I was trying to generate the code quickly whilst writing the guide, to provide samples of code as the thread was being written.

Point 2.

Essentially yes, the second check is redundant but it is supplied for good reason. Normally, when I write an Event Handler, I may need to re-use that code for several objects, each may respond to different triggers and pass back different values depending on those triggers.

Therefore, it is always wise to write your code to check for each specific scenario that it should handle, prior to executing the necessary code, even if there’s only 1 CURRENT scenario likely. The check itself is very fast, especially a comparison chack of this nature, so it doesn’t really incur any performance hit.

Too many times in the past I’ve written code for a scoped project, only for that project to change many times afterward, with unexpected consequences. With multi-threaded projects, it is often very difficult to track down the actual cause of a problem when debugging. So as a general rule, I’ve learned to never take anything for granted and always check that what I’m expecting is the same as what I’ve been given.

To explain this with a pseudo-example, imagine I want to process the following SimConnect events by simply passing them all back to the same Event Handler:

  1. Connect

If I write the Message Pump to check for any Sim Messages (any type) and raise an event for this scenario, I could return them all to a single Event Handler and let that decide what to do with each of the messages I pass to it. This would ensure the code for the Message Pump that was raising this, would remain fairly/very static, with no need for future updates, any changes to our requirements could then be done within the Event Handler itself, as everything is passed to it.

If I was then asked to include a new Event Handler to process something like Sim Exceptions, this too could be passed to the same Event Handler. But if the Event Handler always assumed I was only ever receiving Connect messages, my Event Handler would fail, as it didn’t check which event was being used to call it.

I apologise if this is a little confusing, I’m a bit rushed to write the response this morning as I’m about to head off to work, but hopefully the above will help you understand why the code was written in this way. You’re welcome to write code any way you wish, there’s no real wrong or right approach if the code works, I just write my own code using a “belt-and-braces” approach to avoid potential problems arising further down the development path.

Jim

Hello Jim,

Thanks for spending your morning in answering me :slight_smile:. Really appreciated. And I’m happy to see that I did understand all of it. But the cancellation of the messagePump Task seems not to be working, and the reason is obvious. Let me explain (and maybe you know all this already, in that case, it might be interesting for other readers).

You provide a CancellationToken to the task.

messagePump = new Task(RunMessagePump, token);

But looking at your code, I assume that you think that by calling “token.Cancel()”, the task automatically gets terminated, and removed from memory? But that’s not the case. The CancellationToken mechanism uses a “cooperative cancellation model”, which means that your task should check on a regular basis the property CancellationToken.IsCancellationRequested (at least, that is one of the ways to do it), and if this is true, you exit the Task. It means that the task has to “cooperate” in the cancellation. You find some in-depth explanation here: Cancellation in Managed Threads | Microsoft Docs.

The problem is that the last call in your messagePump Task is:

Application.Run(); // Begins running a standard application message loop on the current thread.

This brings your task in an endless loop, because Run() never exists. The call to “token.Cancel()” has no effect at all, because nowhere this cancellationtoken is evaluated and exits the Application.Run() call. This means that every time you do a connect/disconnect cycle, a new messagePump Task is being created.

I have searched already several hours, but couldn’t find any clue on how to exit the Application.Run() call triggered by means of a CancellationToken.

Hans

Hello @Dragonlaird,

Just find out something!!! Seems you are using a “Windows Forms App” based on “.NET 5.0”, and not “Windows Forms App (.NET Framework)” which I was using, allowing me only to select “.NET Framework 4.7.2” as highest version.

I realized this when I got stuck to the below line of code that did not want to compile:

image

In my version, the signature “TEnum Parse<TEnum>(string value)” was not recognized. Scrolling up in the metadata I found that you used .NET 5.0. Took me more than 1 hour to figure out what was going on.

I’m afraid I will have to recreate my projects, because I can’t simply transfer from one type of project to another. It now also explains why the Solution Explorer in your version looked a bit different than mine.

Ok, I don’t need .NET 5.0 to use SimConnect I guess, but if I want to continue to explore your code and syntax, I have to change it.

Step by step I’m getting there… :slight_smile:

Greetz,

Hans

It would if I had remembered to include the following line in the Message Pump:

token.ThrowIfCancellationRequested();

Oops…

I used the latest library available at the time to create the project, with the aim of extending the lifespan of the code (and therefore the topic). I can’t remember the number of times I’ve googled for a solution to a problem, only to find the “solutions” I found are so old, they need a complete rewrite to work in the latest code.

Guess this is one time where the code was “too new” lol

If you want to convert an integer to an ENUM value in .Net Framework (assuming the int value is one of the ENUM entries you want), just cast it to the ENUM type you want:

If the int value is outside the range of ENUM entries, the above would likely cause an exception, however, this check has already been done in the surround “if()”

Don’t think that works Jim. Just adding this line in the Message Pump makes no difference.

token.ThrowIfCancellationRequested();

I think some hocus pocus with this async/wait magic needs to happen. Still, no success :frowning:

Not that it is such a big issue, because to be honest, my application only needs to connect and remains connected endlessly. There should even be no Disconnect (or it means I’m stopping MSFS2020, and shutdown my cockpit), but it just doesn’t feel ok that a Task is running and we can’t get rid of it if we want.

Hello @Dragonlaird,

My daily questions :slight_smile:. No need to answer asap you know, just take your time (or even ignore me if I ask to much). I leave the cancellation stuff behind me now - it doesn’t work… but it doesn’t matter. I don’t need to disconnect anyway, although Disconnect works perfectly of course, it’s just the Task that I can’t get rid off.

I dissected the next parts of your code and put the SimConnect documentation next to me. Next goal is to receive my first SimVar, and why not using the “AUTOPILOT ALTITUDE LOCK VAR:3” which I know is working. And I had my first successes!!! Thanks to your very educational code.

I just want to keep it very basic, so my registration call looks like this:

public static int RegisterSimVar(string sName, string sUnit, SimConnectUpdateFrequency frequency)

And here I do my 3 calls that are the basic scenario of getting some data from SimConnect:

  1. Call to AddToDataDefinition

simConnect.AddToDataDefinition((SIMVARDEFINITION)0, sName, sUnit, SIMCONNECT_DATATYPE.FLOAT64, 0.0f, SimConnect.SIMCONNECT_UNUSED);

If I’m not mistaken, this is to link a DefineID (which is 0 in my first test) with my variable, and also define what datatype my SimVar is.
It is even possible to use DefineID with complete structures, where each element of the structure is a seperate SimVar (a lot of examples show a structure with “latitude”, “longitude”, …). In that case you do successive calls to AddToDataDefinitions. Looks like the different SimVars are added in sequence.

Question:
What is this last parameter DatumID which is getting the value “SimConnect.SIMCONNECT_UNUSED”? The SDK documentation says “Use this to identify the data received if the data is being returned in tagged format”. But I’m also puzzled with passing it the value “SimConnect.SIMCONNECT_UNUSED” (by the way, your code also seems to have a “SimConnectHelper.SIMCONNECT_UNUSED”?). But in both cases, this is a variable, and not just some constant:

public static uint SIMCONNECT_UNUSED;

Ok, assuming that this variable initializes to 0 somewhere, but if not, this can be any value right?

  1. Call to RegisterDataDefineStruct

simConnect.RegisterDataDefineStruct<double>((SIMVARDEFINITION)0);

It’s crazy to see how little information you can find if you Google - it’s so bad documentation! But it looks like this is telling SimConnect what type my variable identified with DefineID has. In a lot of cases this might be “double”, but even complex structures can be used.

No questions here.

  1. Call to RequestDataOnSimObject

simConnect.RequestDataOnSimObject((SIMVARREQUEST)0, (SIMVARDEFINITION)0, 0, (SIMCONNECT_PERIOD)frequency, SIMCONNECT_DATA_REQUEST_FLAG.DEFAULT, 0, 0, 0);

If I’m not mistaken, this connects my earlier defined DefineID with a RequestID, defines the frequency (how fast SimConnect sends me an update) and some other flags or parameters that I ignore here.

Questions 1
I think that the SimConnect SDK Documentation is wrong here. It says that DefineID is the group’s priority, but that feels like complete nonsense in this context, right?

Question 2:
There is also a “RequestDataOnSimObjectType”, but that seems to look for variables of a certain type in a certain radius? The purpose of my tool is to connect my Cockpit Hardware with MSFS2020, so I don’t think that this implementation makes sense, right?

  1. The result

And yes!!! My WndProc in my MessageHandler is receiving a WM_USER_SIMCONNECT every second, and is invoking this to the method “MessageReceived”. There a call to “SimConnect.ReceiveMessage” is done, which seems to instruct SimConnect to invoke a call to “SimConnect_OnRecvSimobjectData” in which I get the data.

The data I’m receiveing has the structure "SIMCONNECT_RECV_SIMOBJECT_DATA ". I still need to puzzle how to deal with that data, but I can already see with the debugger that the member dwData is an array, and the first element seems to contain the altitude of my plane. YIHAAAA!!!

Question:
Do you have some documentation on this data structure, and how I have to interpret this dwData array? Anyway, if I use Debug.WriteLine on it, I can see that data.dwData[0] does contain my altitude! :nerd_face: I assume some mapping needs to be done on the previous structure that has been defined (In my case, this is only a double). But what if I would have a structure, or a string? Is dwData[0] always pointing to my data as an object (double, structure, string, …), but why do I need dwData[1], dwData[2]… Many questions here.

Hi Hans,

I’ll try and answer what I can from both of your posts here. We’re starting to venture into discovering the main reason this topic was created initially, to help people overcome the lack of information available on how to use the SDK etc.

Quite possible, I’ve added a new issue to the project repo to remind me to fix this. I don’t think it will be difficult, just a lack of time prevents me tweaking this project just now.

I recall being confused by this myself at the time I wrote the code, from a hazy memory, I think this relates to an internal working of how MSFS updates and stores the values of each SimVar. The following may not be 100% accurate as it is from a hazy memory of when I was investigating various things related to this and I have no idea where I got this info from.

To explain in a little more details, MSFS doesn’t just go grab each requested value and send it to us on a timed schedule, the values it holds are kept in an interal array of some kind, which is referened by DefineId (it has multiple arrays for different groups of variables).

This array is updated by MSFS on a regular basis (not the same frequency as that requested, I’ve no idea how often this is done, or even if each value is updated independantly). Then, when our schedule time is reached for each variable, all the values we’ve requested for that frequency are sent back to us.

This approach minimises the risk of SimVar requests having a stuttering impact on MSFS itself, as the values are already updated when they need to be sent, rather than MSFS having to loop through our requests and fetching them and updating its local cache before streaming them to our client.

When we want to cancel a submitted SimVar request we reference the DefineId, which allows MSFS to destroy the entire array and release the used memory.

Thus, it helps MSFS prioritise values and groups them together to simplify their removal, minimising any impact in the main UI.

Yes, it allows you to detect specific SimVar types outside of your aircraft within a specified range. Although I can’t think of a specific MSFS example of when this might be used (maybe waypoints?), it reminds me of something we used to develop many years ago when we used “sprites” for defining images and move them around the screen. Something like this would have been used for collission detection, to advise us when sprites of a certain type had intersected with other sprites, which may be allowed for some sprites but not others, e.g. some might be allowed to overlay or sit behind another - such as moving stars allowed to disappear behind a space ship, but another space ship would actually collide.

MSFS documentation explains in a little more detail how to read this structure. I note thie docs have been improved since I last reviewed them - yippee!

https://docs.flightsimulator.com/html/index.htm#t=Programming_Tools%2FSimConnect%2FAPI_Reference%2FStructures_And_Enumerations%2FSIMCONNECT_RECV_SIMOBJECT_DATA.htm&rhsearch=SIMCONNECT_RECV_SIMOBJECT_DATA&rhhlterm=SIMCONNECT_RECV_SIMOBJECT_DATA

If I remember correctly, some SimVars return a structure instead of a discrete value - e.g. requesting an object (or aircraft) position would be fairly useless if it only returned a single numeric value. The index approach allows several values to be supplied for a single SimVar request, such as Lat. Long. Alt. etc

Hope this helps, can’t guarantee 100% accuracy in all my replies, if any are misleading I apologise and doff my cap to anyone who provides a better answer.

Jim

Quick one regarding this. You can generate tokens using the same source. This may be used for multi-threaded applications, where you start and stop threads independently of each other.

The problem then is, when you wish to dispose of the main application, how to remember which tasks are running and which tokens to cancel.

Here’s where our Source comes in handy. Since all your tokens were generated by a single Source, we can simply tell that to cancel any tokens it has generated.

Voila - all tasks shutdown cleanly.

Sadly, as you’ve already discovered, the full code required to implement tokens isn’t there yet - one of those jobs that will be done when I get “a round tuit” :slight_smile: :rofl:

Hello Jim,

I interpret this as a kind of “green light” to continue posting my questions in this post :slight_smile: So here we go!

1. Question about ClientData

Talking about information or documentation, I looked at SimConnect References (prepar3d.com) as well. At least, the explanation of RequestDataOnSimObject seems to give the correct explanation about DefineID.

I now try to get a better understanding of the mechanisms used in SimConnect. If I’m not mistaken, you have a set of SimObject Functions, which seem to directly get data of a specific object. One of these functions is RequestDataOnSimObject, which is used in the above explanation, and seems to directly provide me with data on the SimVars as defined with DefineID.

But there are also a lot of functions that relate to ClientData, which seems a way to reserve a certain space that can be used to exchange data? Do you have more knowledge on the reason why these functions are there? And what the concept is behind them? If I understand it correctly, this mechanism allows sharing some data between different clients. And this is the reason why this is used together with a WASM module, which is what you need to work with LVars and HVars. Could make sense, because it means that both the WASM module and your application are both clients of SimConnect, and use this ClientData to exchange information (because you can’t talk directly with the WASM module). Anyway, still too early to dig into that matter in detail, but just curious.

2. Question about RegisterDataDefineStruct

And what about this mysterious RegisterDataDefineStruct? This is a function that is not found in the SimConnectSDK, but is only used with managed code. The SDK Documentation says:

Structs are mostly unchanged, with the exception that a char array is represented as a System.String . However, they do need to be registered with the managed wrapper with a call to RegisterDataDefineStruct . If a string is too long during the data marshaling, it will get truncated safely.

I removed the below line from my code, and then things seem not to work any longer. So it is definitly needed.

simConnect.RegisterDataDefineStruct<double>((SIMVARDEFINITION)0);

Do you have some more explanation on this?

3. Question about the WndProc

Is there a specific reason why you don’t directly call “simConnect.ReceiveMessage()”, but that you do that indirectly via the EventHandler MessageReceived?

I think that is it for now.

Greetz,

Hans

(PS - Although I’m addressing my questions to Dragonlaird, other readers are of course also encouraged to give their view on things and give answers!)

Hi Hans,

I’m not able to answer this just now as I’ve family arriving tonight for Xmas, but I didn’t want to leave you thinking you were being ignored.

@Steeler2340 might be able to supply some answers in my absence, he’s very knowledgeable on SimConnect and also WASM (which I’ve no experience with at the moment).

Jim

Really Jim, you helped me already A LOT!!! You should not feel obliged to give answers within 24 hours, definitely not. This is a hobby, so I have time. Enjoy your family!!!

By the way, I have kind of dissected some code to the bare minimum, which makes it really easy to understand a lot of the mechanisms. I will describe it below in another post. I hope that’s ok for you (if not, I delete the post). But I think that these real basics are what we are missing in all the documentation.