Hi all,
I wanted to share a pattern I’ve developed for working with the managed SimConnect API in Microsoft Flight Simulator 2024, especially around handling messages efficiently without burning CPU cycles.
The Problem
The managed SimConnect API requires you to call ReceiveMessage() to trigger callbacks like OnRecvSimobjectData, OnRecvOpen, etc. However, it’s not well documented when to call it, or how to know when messages are available—leading many developers to use polling or blocking loops.
The Solution
SimConnect provides a WaitHandle that gets signaled when messages are available. By wrapping this in an async-friendly construct, you can wait efficiently and only call ReceiveMessage() when needed.
Here’s the core pattern I use:
WaitHandleAsyncWrapper handleAsyncWrapper = new(simReceiveEvent);
while (simConnect != null)
{
using var timeoutCts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(userToken, timeoutCts.Token);
await handleAsyncWrapper.WaitAsync(linkedCts.Token); // Wait for signal
simReceiveEvent.Reset(); // Prepare for next signal
await semaphore.WaitAsync(userToken); // Ensure thread-safe access
try
{
simConnect?.ReceiveMessage(); // Triggers registered callbacks
}
finally
{
semaphore.Release();
}
}
Notes
The WaitHandleAsyncWrapper uses ThreadPool.RegisterWaitForSingleObject internally to await the signal asynchronously.
A timeout ensures the loop stays alive even during idle periods.
Cancellation tokens allow graceful shutdown when the user exits or the sim disconnects.
I’ve observed that COMException is reliably thrown when the simulator exits, which I use to trigger cleanup.
Result
This approach dropped my app’s CPU usage from ~5% to 0% during idle, while keeping message handling responsive and clean.
Hope this helps others navigating the quirks of SimConnect in .NET. Happy flying—and coding!
My latest app can be found at the below link. I have released it as freeware so anyone can have some fun with it! However, I have not published the source code, it is still under consideration. This is the first announcement of it.
Hi, since I haven’t published the source code and since this forum is about the MSFS SDK, I’m not ready to reveal any details about the internal architecture of my app here. But if there are any questions related to the SDK, we can definitely discuss it here! You can actually create a lot of amazing things, more than you can imagine, using it.
I’m not asking about internal details, just curious what framework you used for the exe.
The reason I ask is because if it’s a WPF or WinForms application, then you don’t need to use a blocking or polling mechanism, nor a semaphore, to receive the SimConnect messages. You can just tie into the existing Windows message pump. This has been well-documented since the FSX days.
That’s why I’m curious if you’re using some other framework that doesn’t provide a normal Windows message pump, or if you’re not using any UI framework at all, and it’s a console app.
Well, I use the SimConnect logic in different setups. You might have a client/server approach where the SimConnect stuff is in a server component. Then you can connect different types of clients instead of creating a monolith. I try to create generic components which can be reused in different setups. This is a pure hobby project so I experiment with all sorts of tech and the app I published here can be seen as a monolith but that can change!
The Windows message pump is really old stuff which was used in the good old days when VB and C++ where popular for GUI apps where you struggled will all sorts of problems not really related to the business problems you tried to solve. if you use Windows Forms or WPF today, you really try to avoid that pump! I would like to see an improved SimConnect implementation for managed apps where the ReceiveMessage logic would be hidden inside the SDK keeping devs to even try to mess with it causing all sorts of locking issues.
Gotcha, I was just curious because you said it was not well-documented as to when to call ReceiveMessage, but it has been well-documented since the FSX days. You also mentioned that a lot of developers resort to using polling or blocking loops, and that struck me as odd because I’ve been developing SimConnect apps for many years now and I’ve never seen example managed SimConnect code that uses polling or blocking. That’s mainly because the original SimConnect documentation shows how to use the existing message pump to be notified when there is a message waiting, so nobody ever had to use polling or blocking unless they didn’t have a message pump available, such as if they were writing a console app. (And most managed SimConnect apps do have a GUI.) That’s why I asked what type of UI framework you were using.
I understand but I think a more up-to-date approach is not being dependent on a message pump. I have another app where the GUI is web based and there you don’t have that pump. Also, suppose you want to do something using WASM so that it can be deployed on Xbox, then you have to think of other techniques than used back in the old FSX days. Also note that .NET can be run on different operating systems and processors so I think you should avoid locking yourself into a corner.
Regarding documentation, how to use ReceiveMessage in a managed server component is not documented at all.
Yeah, I agree completely, I was just confused by what you said about there being no documentation about how to retrieve messages, because it has been documented since the FSX days.
You mentioned web-based apps, have you done a web app that connects to the sim via managed SimConnect?
Same question about WASM, have you done managed SimConnect from WASM? I was under the impression that you could not use managed code in WASM.
My WASM is C++, not sure if it is possible to use any other language in MSFS for this. For the web app I have a server component which acts as a web server and is multithreaded du to the nature of HTTP. This requires the SimConnect implementation to be able to handle requests in such an environment where it is easy to create deadlocks since SimConnect is not reentrant.
The newly announced support for PS5 is very interesting. A whole new market opens up for third party devs if they use the right tech and skip the message pump
Ahh, okay, I thought you were talking about a browser-based web app, like a SPA.
Yeah, AFAIK you cannot write WASM modules with managed code. Since you mentioned WASM when we were talking about using the message pump with managed code, I thought you were saying you could use managed code for WASM. I wish that was the case!
If you’re using unmanaged code, is the semaphore still the only way to do non-blocking, non-polling message handling, or does the unmanaged SimConnect API provide something else like a callback mechanism? (I’ve only done managed C# SimConnect dev, so I don’t know what else the unmanaged API provides, if anything.)
Well, the web app is actually a SPA using WASM (not the Asobo variant) and the server is holding the backend logic and also hosting the GUI components based on Kestrel.
For my in sim WASM module (unmanaged code) I use SimConnect to communicate with the external app and is using SimConnect_CallDispatch/SimConnect_GetNextDispatch instead of ReceiveMessage. An alternative to SimConnect when communicating between an in sim WASM module and an external module is using a plain file on the file system. A third option is to use the MSFS Communication API but I have never got that to work. I haven’t found any other ways if you want to pass a bigger chunk of structured data. SimConnect is the most reliable according to my research.
Ahh, yeah, I think SPA isn’t what I meant, rather an entirely browser-based application. E.g. entirely JS or WASM, with no logic on the server side. That’s what I assumed you were talking about when you mentioned a web-based app.
Yes and no. Those calls are indeed what you’d use when polling, but you’d also use them when using Windows Events (such as in the original example above), or even the Windows Message loop. Letting SimConnect put messages on the Window’s message queue doesn’t give you the actual message, just a notification that a message is available, just as with the code provided in this post.
You’ll still have to ask SimConnect for that message, which is where CallDispatch and GetNextDispatch come into play.
The first lets you pass a function that will be called with the message as a parameter, the second asks for the message itself. The difference is subtle: with the first you have a reference to the message in the scope of that function to handle (or copy) the message. With the second you get a reference and can decide how to handle it in your own scope, but you should not ask for the next until you’re done with it. The reference/pointers will in both cases point at some kind of cache inside the SimConnect library’s managed memory, so it will get reused for a new message at some time later on. This is not documented AFAIK, but neither is the fact that SimConnect is not thread-safe, and that is pretty easy to prove. (and gets you some nice “undefined behaviour”)
The .Net (aka “Managed”) library has a ReceiveMessage() method that actually skips the whole “give me the next message” bit. You have to register handlers (event handlers in C# parlance) that will be called as a consequence of the call to ReceiveMessage(). In C/C++ it is up to you to do the dispatching.
The only “documentation” for the .Net library is the SimVarWatcher sample. It uses the Window message handling to get notified, then calls ReceiveMessage().
Yeah, well, it’s not really documentation… More a high-over one-pager on writing .Net apps, with a lot of space spent on showing what the equivalent Visual Basic (.Net version) code could be.
I knew about that page, and I don’t consider it proper documentation for an API. Sorry.