Paul Henty Posted April 27, 2021 Report Posted April 27, 2021 MSFSVariableServices Background A new class called MSFSVariableServices has been added to the FSUIPCClient dll from Version 3.2.1-Beta. This module allows direct access to LVars and HVars for MSFS. Other Flight Sims (e.g. P3D) are NOT supported. It uses John Dowson's WASM module for MSFS which must be installed in your MSFS and will also need to be installed in your users' MSFS. This module is installed by the FSUIPC7 installer. This facility is completely independent of FSUIPC. When using this feature in the FSUIPCClient dll you will not need a connection to FSUIPC. It works separately. Note that the FSUIPCConnection.ReadLVar() and WriteLVar() methods still use the legacy LVAR access provided by Offset 0x0D70. You can still use these, however this method of LVAR access has historically been slow (read one variable per Process()) and will continue to be. The new MSFSVariableServices offers significant performance advantages. Requirements As the developer you will also need John's FSUIPC_WAPID.dll. This can be found in latest the WASM package which can be downloaded from the "Additional Free Software" section of the FSUIPC website: (Look for "FSUIPC WASM Module x.x.x") http://www.fsuipc.com/ Inside the Zip file, the DLL can be found in \FSUIPC_WAPI\dll This dll must be distributed with your application and should be located in the same folder as your .exe and FSUIPCClient.dll. In Visual Studio you can add this DLL to your project as a normal project item and set the property "Copy to Output Directory" to "Copy if Newer". You CANNOT add this dll as a reference to your project as it's an unmanged C-style DLL. 64Bit Only The WAPI DLL is 64 bit so you must run your .NET application as 64-bit. This means the 'Platform Target' must be x64, not x86. You can also target 'Any CPU' but you must untick the box that says "Prefer 32-bit". These options can be found in the project properties page on the Build tab. (Right-Click the project in the solution explorer and select Properties). Example Code: An example project (C# and VB.NET) can be downloaded from the FSUIPCClient DLL website: http://fsuipc.paulhenty.com/#downloads How to Use: The MSFSVaraibleServices is a static class in the FSUIPC namespace. You need to add a using statement for the namespace: using FSUIPC; If you don't want to use the full class name you can optionally add an alias: using VS = FSUIPC.MSFSVariableServices; The rest of the code examples here use this alias. Events: This module is purely event-driven. The following events should be handled: OnValuesChanged will notify you when any of the LVar values in the sim change. VS.OnValuesChanged += VS_OnValuesChanged; private void VS_OnValuesChanged(object sender, EventArgs e) { // At least one LVar value has changed. // Do work with new values. For examples see later in this post } OnVariableListChanged will notify you when the WASM module has discovered new LVars and HVars. VS.OnVariableListChanged += VS_VariableListChanged ; private void VS_VariableListChanged (object sender, EventArgs e) { // Variables have been discovered or changed } OnLogEntryReceived. This event will notify you of log entries being produced by the WASM module. This will give you status updates, errors etc. What you do with the messages is up to you. In the example below I'm writing the log entries to a listbox, but you could show them in the Debug output window or write to your own log. VS.OnLogEntryReceived += VS_OnLogEntryReceived; private void VS_OnLogEntryReceived(object sender, LogEventArgs e) { this.lstLog.Items.Add(e.LogEntry); } Initialisation: After setting up your events, you need to call Init(). This will attempt a connection to the flight sim via SimConnect. The result of this will be returned as a log entry. You need to pass in the handle of the main window in your application: e.g: Assuming the code is in the code behind the main form/window: WinForms: VS.Init(this.Handle); WPF: VS.Init(new WindowInteropHelper(this).Handle); Configuration: There are a number of properties to control how the module behaves. The default values for these properties are good for most cases. VS.LogLevel Defines what kinds of events (if any) are logged via the logging system. Values can be: DISABLE_LOG LOG_LEVEL_INFO LOG_LEVEL_BUFFER LOG_LEVEL_DEBUG LOG_LEVEL_TRACE ENABLE_LOG VS.SimConfigConnection By default the MSFSVariableServices will connect to the local instance of SimConnect. If you have other instances configured on your machine (e.g. connections to remote PCs running MSFS) you can change this property to use them. Starting the module: After initializing and setting your configuration, you must then call Start() to tell the WASM module to start receiving data. VS.Start(); NOTE: After calling Start() it will take the WASM module a few seconds to collect the data from the flight sim and do its housekeeping. This means that LVars and HVars may not be instantly available. It's best to wait until the OnVariableListChanged event has fired before reading LVars. If you ever want to pause the data transfer you can call VS.Stop(); No more data updates will be received by the WASM module until you call Start() again. Accessing LVars: The module automatically discovers the loaded LVars. These are located in the LVars property and are stored as a collection of FsLVar objects. You can access the individual FsLVar objects like this: 1. Iterate through all of them: foreach (FsLVar lvar in VS.LVars) { // Do something with each LVar... e.g. CheckLVar(lvar); } 2. Get a specific LVar by name: FsLVar myLVar = VS.LVars["B747_8_MFD_Range"]; Getting names of all LVars You can find a List<string> of all known LVar names at: VS.LVars.Names Checking if LVars exist To find if an LVar exists by Name use: if (VS.LVars.Exists("B747_8_MFD_Range")) { // Exists } Counting number of LVars You can get the number of LVars with: VA.LVars.Count Accessing HVars HVars are stored as a collection in the property VS.HVars This collection works in exactly the same way as LVars above. Using the LVar values: Each FsLVar object has a Value property. This is a double type as all LVars are stored as doubles within the Flight Sim. e.g. To get the value for B747_8_MFD_Range use: double range = VS.LVars["B747_8_MFD_Range"].Value; Advanced use: You can also keep a reference to the LVar FsLVar mfdRange = VS.LVars["B747_8_MFD_Range"]; double range = mfdRange.Value; Updating the LVar values: To write a new value for an LVar call the SetValue() method. This will sent the new value to be written to the Flight Sim. e.g. VS.LVars["B747_8_MFD_Range"].SetValue(2); NOTE: This will NOT update the local Value property of the FsLVar. It only sends a request to the Sim to change the value. The local Value property will be updated when the Sim sends back the new value. The OnValueChanged event will also fire at this time. Setting HVars: To set an HVar, call the Set() method of the FsHVar object: VS.HVars["AS1000_PFD_NAV_Switch"].Set(); This will cause the new HVar to be set in the Flight Sim immediately. Checking for changes in LVars: There are a number of ways to know if LVar values have changed: 1. Check the ValueChanged property of the FsLVar object: if (VS.LVars["B747_8_MFD_Range"].ValueChanged) { // do something with new range value } 2. Get a collection of all the changed LVars from the LVarsChanged property: foreach (FsLVar lvar in VS.LVarsChanged) { // Do something with each changed LVar... e.g. NotifyChangesTo(lvar); } Note that the LVarChanged collection works the same way as the normal LVars collection. 3. You can handle the OnValueChanged event of an FsLVar: e.g. to be notified of changed to B747_8_MFD_Range: VS.LVars["B747_8_MFD_Range"].OnValueChanged += MFDRange_OnValueChanged; private void MFDRange_OnValueChanged(object sender, LVarEvent e) { double newSetting = e.LVar.Value; MessageBox.Show("MFD Range Changed to " + newSetting.ToString()); } Logging LVars and HVars: List of LVars and HVars can sent to the log by calling: VS.LogLVars(); VS.LogHVars(); Creating a new LVar: Call CreateNewLVar passing in the name of the new LVar and its initial value. VS.CreateLVar("My_New_LVar", 1.25); Executing Calculator Code: VS.ExecuteCalculatorCode(myCode); where myCode is a string containing the code to execute. Paul
kingm56 Posted May 3, 2021 Report Posted May 3, 2021 Hello, My Friend! Is the beta 3.2.1-Beta available via NuGet package?
Paul Henty Posted May 8, 2021 Author Report Posted May 8, 2021 On 5/3/2021 at 5:59 PM, kingm56 said: Hello, My Friend! Is the beta 3.2.1-Beta available via NuGet package? Yes, but for Beta versions you must check the box "Include Pre-release" in the Nuget manager.. Paul
kingm56 Posted May 8, 2021 Report Posted May 8, 2021 Paul, I continue get a "An attempt was made to load a program with an incorrect format." The FSUIPC_WAPID.dll is located inside the same folder as the .exe and FSUPIC.dll. Any ideas?
Paul Henty Posted May 8, 2021 Author Report Posted May 8, 2021 Make sure your exe is compiling and running as a 64 bit process. The FSUIPC_WASPID.dll is 64bit so cannot be loaded from a 32-bit process. In your exe project properties, go to the Build tab. Make sure your platform target is either x64 or "any cpu". If it's "any" make sure the box for 'prefer 32bit" is NOT checked. Paul
kingm56 Posted May 8, 2021 Report Posted May 8, 2021 Thank you, sir! I'm able to instantiate the object now; however, I'm still getting a object reference not set to an instance error...even when initializing it first. Below is just a simple method to obtain A320 battery state: MSFSVariableServices VS = new MSFSVariableServices(); VS.OnLogEntryReceived += VS_OnLogEntryReceived; VS.Init(this.Handle); VS.Start(); Thread.Sleep(2000);//gives a few seconds to collect the data from wasm module double batt = VS.LVars["A320_Neo_BAT_State"].Value; MessageBox.Show(batt.ToString());
Paul Henty Posted May 8, 2021 Author Report Posted May 8, 2021 You'll need to add a call to VS.RefreshData() after the sleep, and before you try to read the LVAR. If it still doesn't work try the following: 1. Waiting longer than 2 seconds. 2. Check the list of LVar names at VS.LVars.Names to see all the LVars that have been found. (If you have logging set up you could also use VS.LogLVars()) Paul
kingm56 Posted May 9, 2021 Report Posted May 9, 2021 Hey Paul! MSFSVariableServices VS = new MSFSVariableServices(); VS.OnLogEntryReceived += VS_OnLogEntryReceived; VS.Init(this.Handle); VS.LogLVars(); VS.Start(); Thread.Sleep(5000);//gives a few seconds to collect the data VS.RefreshData(); try { MessageBox.Show(log); } catch(Exception ex) { MessageBox.Show(ex.ToString()); } Apparently I'm only logging 000 LVARS. Any other ideas, my friend?
Paul Henty Posted May 9, 2021 Author Report Posted May 9, 2021 You need to call loglvars after you call refreshdata.
kingm56 Posted May 9, 2021 Report Posted May 9, 2021 Sorry to be nausaunse, Paul; however, I'm still unable to read any LVARS. I continue to get this: public partial class Dashboard : Form { MSFSVariableServices VS = new MSFSVariableServices(); ..... public Dashboard() { try { InitializeComponent(); Connect.Click += Button_Click; VS.OnLogEntryReceived += VS_OnLogEntryReceived; } catch (Exception) { } } private void debugToolStripMenuItem_Click(object sender, EventArgs e) { VS.Init(this.Handle); VS.Start(); Thread.Sleep(5000);//gives a few seconds to collect the data VS.RefreshData(); VS.LogLVars(); Thread.Sleep(2000);//gives a few seconds to collect the data try { MessageBox.Show(log); } catch(Exception ex) { MessageBox.Show(ex.ToString()); } }
jaxx Posted May 9, 2021 Report Posted May 9, 2021 I have written a small class that handles the connection starting, connection error handling etc. Reason is my app is constantly running and making requests and should not be impacted if the sim is not running or starting or shut down in between. With the FSUIPCConnection I could just check if the connection is open, if not try to open it and if there were an error (e.g. FSUIPC was not running) I would get some kind of exception and the app would wait a bit and try again. With the new MSFSVariableServices there seems to be no immediate failure (e.g. when start fails), instead the information is only then received in the log entry message events. Hence why I wrote this handler class. Usage is quite simple, when you run code through the "Try"-methods it will make sure the connection is available, start the connection if it's not and give a result if your action was successful. In case of an error it will try to stop the connection so on the next request it will try to start it again. Examples: // Initialize the wrapper. You still need your handle (e.g. this.Handle from Windows.Forms // or from a custom Messaging component in a console app) and optional a handler for messages VariableHandler variables = new VariableHandler(handle, msg => Console.WriteLine(msg)); // As a connection start is implicitly ensured before each action this is enough to perform // an initial start (e.g. if you want to allow for time to gather the data before your first // real request): variables.Try(vs => {}); // Reading all LVars: variables.Try(vs => { vs.RefreshData(); Console.WriteLine(vs.LVars.Count + " LVars:"); foreach (FsLVar lvar in vs.LVars) { Console.WriteLine(lvar.Name + " = " + lvar.Value); } }); // Do a refresh: variables.Try(vs => vs.RefreshData()); // Reading a single LVar: if (variables.Try(vs => vs.LVars["LVAR_NAME"], out FsLVar lvar)) { if (lvar != null) { Console.WriteLine(lvar.Name + " = " + lvar.Value); } } // Force a connection stop (something that should not be necessary usually): variables.Try(vs => vs.Stop()); This is not a higher level abstraction layer, as you still work with the normal MSFSVariableServices object through the "Try"-methods, but you don't have to care about managing the connection or handling an Exception that occurred because you sent a request after the sim has shut down etc. It's not much but I think it could help someone having similar problems. VariableHandler.cs
Paul Henty Posted May 9, 2021 Author Report Posted May 9, 2021 Your code looks okay now. Can you please try running John's WASMClient.exe program to check if that is returning any LVars. You can find it in the FSUIPC-WASMv0.4.10.Zip file. Paul
jaxx Posted May 9, 2021 Report Posted May 9, 2021 1 hour ago, kingm56 said: Sorry to be nausaunse, Paul; however, I'm still unable to read any LVARS. I continue to get this: Have you tried not using LogLVars and instead simply accessing the LVars directly? Very simple WindowsForms example: MSFSVariableServices VS; private void Form1_Load(object sender, EventArgs e) { VS = new MSFSVariableServices(); VS.OnLogEntryReceived += VS_OnLogEntryReceived; VS.Init(this.Handle); VS.Start(); } private void Button1_Click(object sender, EventArgs e) { VS.RefreshData(); string text = VS.LVars.Count + " LVARS\r\n"; foreach (FsLVar lvar in VS.LVars) { text += lvar.Name + " = " + lvar.Value + "\r\n"; } MessageBox.Show(text); } private void VS_OnLogEntryReceived(object sender, LogEventArgs e) {}
kingm56 Posted May 10, 2021 Report Posted May 10, 2021 4 hours ago, Paul Henty said: Your code looks okay now. Can you please try running John's WASMClient.exe program to check if that is returning any LVars. You can find it in the FSUIPC-WASMv0.4.10.Zip file. Paul Hey Paul! I tried running WASMClient.exe; the WASM Client connected, but does not collect any LVARS, which appears to be my culprit. Here's the log entried: Sun May 09 2021 17:17:35.385 [INFO]: Connected to MSFS Sun May 09 2021 17:25:24.355 [INFO]: SimConnect_Close done
kingm56 Posted May 10, 2021 Report Posted May 10, 2021 3 hours ago, jaxx said: Have you tried not using LogLVars and instead simply accessing the LVars directly? Very simple WindowsForms example: MSFSVariableServices VS; private void Form1_Load(object sender, EventArgs e) { VS = new MSFSVariableServices(); VS.OnLogEntryReceived += VS_OnLogEntryReceived; VS.Init(this.Handle); VS.Start(); } private void Button1_Click(object sender, EventArgs e) { VS.RefreshData(); string text = VS.LVars.Count + " LVARS\r\n"; foreach (FsLVar lvar in VS.LVars) { text += lvar.Name + " = " + lvar.Value + "\r\n"; } MessageBox.Show(text); } private void VS_OnLogEntryReceived(object sender, LogEventArgs e) {} Hey Jaxx! It appears WASMClient isn't collect any LVARs. Any thoughts, my friend?
Paul Henty Posted May 10, 2021 Author Report Posted May 10, 2021 Quote the WASM Client connected, but does not collect any LVARS, There seems to be a problem somewhere other than your code/my dll then. The best thing to do would be to ask John about this in the MSFS support forum under a new topic. If you tell him the WASMClient.exe is connecting but not finding any LVARs he may be able to suggest things to check, Paul
PapaTus Posted May 10, 2021 Report Posted May 10, 2021 Hi everyone, I developed MSFS software with C #, I tried following the instructions in this post and I was able to set HVars, but after I fixed it, now it doesn't work and it crashes as pictured. I tried WASMClient and it still works fine. Now i use -FSUIPC 7.10 -fsuipcClient 3.2.2.364 -FSUIPC-WASMv0.4.10 And use FSUIPC_WAPID.DLL from it. Can anyone advise me to fix this issue? Best regards, patcharapol Phophun
Paul Henty Posted May 10, 2021 Author Report Posted May 10, 2021 Quote now it doesn't work and it crashes as pictured. It crashes because that HVar doesn't exist. You can see the list of LVARs and HVARs found by using LogHVars() and LogLVars(). What do they show? Remember that you need to wait a few seconds after Start() before the WASM module is ready. Paul
PapaTus Posted May 11, 2021 Report Posted May 11, 2021 8 hours ago, Paul Henty said: It crashes because that HVar doesn't exist. You can see the list of LVARs and HVARs found by using LogHVars() and LogLVars(). What do they show? Remember that you need to wait a few seconds after Start() before the WASM module is ready. Paul Thank you very much for your advice. Best regards, patcharapol Phophun
Paul Henty Posted May 21, 2021 Author Report Posted May 21, 2021 Hi everyone, John made some very useful changes to the WAPI DLL. This has enabled me to improve the way the MSFSVariableServices works. See version 3.2.3-Beta of my DLL. Note that you will need version 0.5.0 or later of the FSUIPC_WAPID.dll. (See the first post for the location of the download). The first post in this thread has been updated to include the new changes and also a quick-start example. The main change is that RefreshData() has now been removed. You don't need to call this anymore because it's now purely event-driven. The MSFSVariableServices module will now automatically maintain the current LVar values in the background (according to the LVARUpdateFrequency property). Two new events are now available: OnVariablesReadyChanged will tell you when the variable have been discovered by the WASM module and it's ready to use. (Check the OnVariablesReady property). OnValuesChanged will tell you when at least one of the LVar values has been changed. I can't test here so please report success/failure here. Thanks, Paul
stevesuk Posted May 29, 2021 Report Posted May 29, 2021 I've tried your new code (as indicated at the top of this post) and neither OnVariablesReadyChanged or OnValuesChanged gets called. (WASMClient indicates that variables are avaliable). Steve.
Paul Henty Posted May 29, 2021 Author Report Posted May 29, 2021 Thanks for the report. I think I've found the problem. Please try 3.2.5, now available on NuGet. Paul
stevesuk Posted May 29, 2021 Report Posted May 29, 2021 OnVariablesReadyChanged and OnValuesChanged now get called (after the VS.Start) but then system fails with this error:- [INFO]: **** Starting FSUIPC7 WASM Interface (WAPI) version 0.5.1 [INFO]: Connected to MSFS An unhandled exception of type 'System.ExecutionEngineException' occurred in Unknown Module. I have no code in the OnVariablesReadyChanged and OnValuesChanged methods. Steve.
stevesuk Posted May 30, 2021 Report Posted May 30, 2021 More testing ... My previous post was Net5. Not a very helpful error message. If I use Net Framework 4.8 I get this error:- [INFO]: **** Starting FSUIPC7 WASM Interface (WAPI) version 0.5.1 [INFO]: Connected to MSFS Managed Debugging Assistant 'CallbackOnCollectedDelegate' A callback was made on a garbage collected delegate of type 'fsuipcClient!FSUIPC.WAPI+NotifyLVarChangedCallback::Invoke'. This may cause application crashes, corruption and data loss. When passing delegates to unmanaged code, they must be kept alive by the managed application until it is guaranteed that they will never be called. The program '[20936] WpfApp3.exe' has exited with code -1 (0xffffffff). Hope it helps Steve.
Paul Henty Posted May 30, 2021 Author Report Posted May 30, 2021 Yes, that one is much better. Thanks. I'll look at this this evening. Paul
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now